• top k 算法


    对于一个非有序的数组A[p..r],求数组中第k小的元素。

    如何考虑

    排序(部分排序)就不用说了。。o(nlgn),当然如果在实际情况中要一直取值,当然要排序后,一次搞定,以后都是O(1)

    我们这里提供了取一次最K小的一个o(n)的解法,
    用了快速排序的一种思想,
    关键在于划分只一个部分,我们知道快速排序选择一个pivot对数组进行划分,左边小于pivot,右边大于等于pivot,
    所以我们计算左边小于pivot(加上pivot)的个数count总共有多少,如果等于k,正是我们所要的,如果大于k,说明第k小的
    数在左边,那就在左边进行我们的递归;否则,在右边,那么说明右边的第k-count小的数就是我们所要的,在右边进行
    我们的递归。

    3.代码
    当k>(r-p+1),返回最大值,当k<1,返回最小值

    #include<iostream>
    using namespace std;
    #include<time.h>
    
    inline void Swap(int a[],int i,int j)
    {
        int temp=a[i];
        a[i]=a[j];
        a[j]=temp;
    }
    
    int Partition(int a[],int p,int r)//根据pivot a[r]来划分数组
    {
        int pivot=a[r];
        int low=p-1;
        int i;
        for(i=p;i<r;i++)
        {
            if(a[i]<=pivot)
                Swap(a,++low,i);
        }
        Swap(a,++low,r);
        return low;
    }
    
    int RondomPartition(int a[],int p,int r)
    {
        int i=p+rand()%(r-p+1);//随机选择一个pivot来划分
        Swap(a,i,r);
        return Partition(a,p,r);
    }
    
    int SelectKMin(int a[],int p,int r,int k)
    {
        if(p==r)
            return a[p];
        int q=RondomPartition(a,p,r);
        int count=q-p+1;//计算 a[p..q]的元素数量 
        if(k==count)//刚好,返回
            return a[q];
        else if(k<count)//在前半部分
            return SelectKMin(a,p,q-1,k);
        else //在后半部分
            return SelectKMin(a,q+1,r,k-count);
    }
    
    
    const int N=100;
    const int K=10;
    int a[N];
    
    int main()
    {
        int i;
        for(i=0;i<N;i++)
            a[i]=i;
        srand(unsigned(time(NULL)));
        for(i=0;i<K;i++){//获得0-100不重复随机数
            Swap(a,i,i+rand()%(N-i));
        }
        int k;
        while(cin>>k)
        {
        cout<<SelectKMin(a,0,K-1,k)<<endl;
        }
        return 0;
    }
    本文实例讲述了C++实现查找中位数的O(N)算法和Kmin算法,分享给大家供大家参考。具体方法如下:
    利用快速排序的partition操作来完成O(N)时间内的中位数的查找算法如下:
    ?
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    48
    49
    50
    51
    52
    53
    54
    55
    56
    57
    58
    59
    60
    61
    62
    63
    64
    65
    66
    67
    68
    69
    70
    71
    72
    73
    #include <iostream>
    #include <cassert>
    #include <algorithm>
    #include <iterator>
     
    using namespace std;
     
    int array[] = {1, 2, 10, 8, 9, 7, 5};
    const int size = sizeof array / sizeof *array;
     
    int partition(int *array, int left, int right)
    {
     if (array == NULL)
     return -1;
     
     int pos = right;
     right--;
     while (left <= right)
     {
     while (left < pos && array[left] <= array[pos])
      left++;
     
     while (right >= 0 && array[right] > array[pos])
      right--;
     
     if (left >= right)
      break;
     
     swap(array[left], array[right]);
     }
     swap(array[left], array[pos]);
     
     return left;
    }
     
    int getMidIndex(int *array, int size)
    {
     if (array == NULL || size <= 0)
     return -1;
     
     int left = 0;
     int right = size - 1;
     int midPos = right >> 1;
     int index = -1;
     
     while (index != midPos)
     {
     index = partition(array, left, right);
     
     if (index < midPos)
     {
      left = index + 1;
     }
     else if (index > midPos)
     {
      right = index - 1;
     } 
     else
     {
      break;
     }
     }
     
     assert(index == midPos);
     return array[index];
    }
     
    void main()
    {
     int value = getMidIndex(array, size);
     
     cout << "value: " << value << endl;
    }
    寻找kmin算法如下:
     
    15
    
    int findKMin(int *array, int size, int k)
    {
     if (array == NULL || size <= 0)
     return -1;
     
     int left = 0;
     int right = size - 1;
     int index = -1;
     
     while (index != k)
     {
       index = partition(array, left, right);
     
     if (index < k)
     {
       left = index + 1;
     }
     else if (index > k)
     {
       right = index - 1;
     } 
     else
     {
      break;
     }
     }
     
     assert(index == k);
     return array[index];
    }
    希望本文所述

    利用最小堆

    此种方法为常用方法,建立一个大小为K的最小堆。每次遍历数组时,需要判断是否需要加入堆中。
       堆中存储着的是最大的k个数字,但是若是需要插入堆中,则需要对堆进行调整时间为o(log k)
       全部的时间复杂度为o(n * log k)。
       
      这种方法当数据量比较大的时候,比较方便。因为对所有的数据只会遍历一次,第一种方法则会多次遍历
      数组。 如果所查找的k的数量比较大。可以考虑先求出k` ,然后再求出看k`+1 到 2 * k`之间的数字,然后
      一次求取。

    #include<iostream>
    using namespace std;
    
    
    void minHeapify(int a[],int heapSize,int i)
    {
        int l=2*i+1;
        int r=2*i+2;
        int smallest;
        if(l<heapSize && a[l]<a[i])
            smallest=l;
        else
            smallest=i;
        if(r<heapSize && a[r]<a[smallest])
            smallest=r;
    
        if(smallest!=i)
        {
            swap(a[i],a[smallest]);
            minHeapify(a,heapSize,smallest);
        }
    }
    
    void bulidMinHeap(int a[],int n)
    {
        int heapsize=n;
        for(int i=n/2-1;i>=0;i--)
            minHeapify(a,heapsize,i);
    
    }
    #define aSize 10;
    
    int main()
    {
        int a[10]={4,1,3,2,16, 9,10,14,8,7};
        int n=sizeof(a)/sizeof(a[0]);
        int k=3;
        bulidMinHeap(a,k);
        //如果X比堆顶元素Y小,则不需要改变原来的堆  
            //如果X比堆顶元素Y大,那么用X替换堆顶元素Y,在替换之后,X可能破坏了最小堆的结构,需要调整堆来维持堆的性质 
        for(int i=n-1;i>=k;i--)//>=k,因为第0个也存
        {
            if(a[0]<a[i])
            {
                swap(a[0],a[i]);
                minHeapify(a,k,0);
            }
        }
        for(int i=0;i<k;i++) 
            cout<<a[i]<<endl;
    
    
    }

    上面的输出:10,16,14,可以看到第k大的存在a[0],为10.

    http://blog.csdn.net/sjf0115/article/details/8603441

    http://blog.csdn.net/rein07/article/details/6742933

  • 相关阅读:
    看雪-课程-加密与解密基础
    Windows API-Wininet&WinHTTP
    OS-Windows-bat-不等待当前命令返回继续执行后续指令
    Code-OPC DA- OPC Client Code Demo
    OS-Windows-Close Windows Error Reporting
    C-长度修饰符
    Code-Linux-time_t
    Windows-bat-Path
    Code-C++-CTime&ColeDateTime
    c++命名规范、代码规范和参数设置
  • 原文地址:https://www.cnblogs.com/youxin/p/3364461.html
Copyright © 2020-2023  润新知