寻找无序数组中的第K大数和前K大数

时间:2022-03-18 22:00:53

两个问题互相可以转化。如果可以找到第K大数,那么只再需要O(N)就可以找齐剩余的前K大数。如果可以找到前K大数,那么只再需要O(K)就可以找到第K大数。


先排序,在找第K个。O(NlgN)


快速排序的思想,可以做到平均效率O(N)

随机选一个元素,把所有小于等于这个元素的数移到左边,所有大于这个元素的数移动到右边。如果这个元素成了第K个数,直接返回这个数。

如果左边的个数大于K,不管右边的数了,在左边重复上面的过程。

如果左边的个数等于T<K,不管左边的数了,重复上面的过程,只是K=K-T-1。


平均情况下,第一次划分的时间复杂度是O(N),第二次是O(N/2),总共是O(N+N/2+N/4+...)=O(N)

#include "stdio.h"
#include "stdlib.h"
using namespace std;
const int INCORRECT=-1;

int FindKth(int d[], int left, int right, int k){
if(left>right) return INCORRECT;
else if(left==right&&k!=1) return INCORRECT;
else if(left==right&&k==1) return d[left];
else if(k>right-left+1) return INCORRECT;

int num=d[left];
int start=left,end=right;
while(start<end){
while(start<end&&d[end]>num) end--;
d[start]=d[end];
while(start<end&&d[start]<=num) start++;
d[end]=d[start];
}
d[start]=num;
int move = start-left + 1;
if(move==k)
return d[start];
else if(move<k)
return FindKth(d,start+1,right,k-move);
else
return FindKth(d,left,start-1,k);
}
int main(){
int data[10]={1,31,4,52,6,4,77,41,62,94};
printf("%d\n",FindKth(data,0,9,9));

}
所以上对于两个问题,理论复杂度都可以做到O(N)。


不过实际上,可能存在数组非常巨大,读取的次数越少越好。如果K比较小,可以用priority queue来存储前K的数。这样只需要读取一遍数组就可以,复杂度是O(N+N*lgK)。


如果N非常巨大,且K=N/2,该如何处理?不知道。