无序整数数组查找第k个最大的数
速览
对于无序整数数组查找第k个最大的数,主要分为如下两种情况
- 内存足够
- 快排
- 二分
- 按数值区间二分
- 按位二分
- 内存不足
- 堆排序(维护一个堆)
- 数据范围取值不大
- 键值索引
算法详解
内存足够
快排
算法思想
避免对所有数进行排序,采用快速排序分堆,并递归其中一半,直到所有<val[pivot]
的数量为k
伪代码
int quickSort(int[]& num,int _begin,int _end){
int i=_begin,j=_end;
int pivot=num[0];
while(i<j){
while(i<j && num[j]<=pivot) j--;
while(i<j && num[i]>pivot) i++;
swap(num[i],num[j]);
}
swap(num[0],num[i]);
return i+1;
}
int FindKth(int[]& num,int k){
int n=num.size();
int curIdx=0;
int l=0,r=n-1;
while(curIdx!=k){
curIdx=quickSort(num,l,r);
if(curIdx<k){
l=curIdx+1;
}else if(curIdx>k){
r=curIdx-1;
}
}
return num[k-1];
}
二分
划定中值区域,以二分法来找到第k个数所在的数值区间。
按数值区间二分
假设N个数的范围为[max_val,min_val]
,则第k个大的数一定在此区间
/* 伪代码 */
while(max_val-min_val>delta){
mid_val=min_val+(max_val-min_val)>>1;
if(count(num,mid_val)>k)
/* count() 返回num中>=mid_val的数量 */
min_val=mid_val;
else
max_val=mid_val;
}
/* 时间复杂度 */ O(N*log(N))
按位二分
假设元素范围均在,则由高位往地位比较,按1、0来分为两个部分,若1区间中整数个数cnt_1>=k
,则在该区间中继续寻找第k大的数;否则在0区间中寻找第k=k-cnt_1
个大的数。
内存不足
上述方法都需要将整个数组遍历一遍,当N非常大时(100亿),上述算法就适用了,故可采用数据结构——小根堆(C++中为优先队列),来解决上述问题。