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

原文地址:https://www.cnblogs.com/youxin/p/3364461.html