总结 -- 寻找最大的K个数,寻找第K大的数

来源:互联网 发布:js 调用原生手机相册 编辑:程序博客网 时间:2024/06/13 07:44

一、在N个数中,寻找最大的K个数

这里只考虑K不等于1的情况,K = 1时,可以通过N - 1次比较和交换得到结果。

 

1. N不大的情况下,几千个左右。

① 先排序,快速排序或者堆排序,平均复杂度为O(N*log2N),再取出前K个,OK)。总时间复杂度,O(N*log2N) + O(K) =O(N*log2N) ; 

 

② 若K <=  log2N,可以进行部分排序(选择排序和冒泡排序)。把N个数中的前K个数排序出来,复杂度是ON*K;

 

③ 寻找N个数中最大的K个数,本质上就是寻找最大的K个数中最小的那个,也就是第K大的数。然后再遍历一次N个数,计算比第K大的数大的数的个数。

 

2. N很大的情况下,100亿

① 用容量为K的数组存放数组的前K个数。读入第K+1个数X,扫描数组,找到数组中的最小数Y,比较YX,如果X大于Y,用X代替Y。如果X小于等于Y,保持原数组不变。时间负责度ON*K

② 进一步,可以用小顶堆来存放着K个数。整个算法的时间负责度为ONlog2K;


 if(x > h[0])    {        h[0] = x;        p = 0;        while(p < x)        {            q = 2 * p + 1;            if(q >= k)                break;            if((q < k - 1)&&(h[q + 1] < h[q]))                q = q + 1;            if(h[q] < h[p])            {                t = h[p];                h[p] = h[q];                h[q] = t;                p = q;            }            else                break;        }}

③ 如果K仍然很大,可以尝试先找到最大的K’个元素,然后找第K’ + 1个到第2 * K’个元素,依次类推。(其中容量K’的堆可以放入内存)。这样,需要扫描所有数据ceilK/K’)次。

 

二、在N个数中,寻找第K大的数

1. 可以使用二分策略。对于一个给定的数p,可以在ON)内找到所有不小于p的数。假如N个数中最大数为Vmax,最小数为Vmin,那么第K大的数一定在区间[Vmax,Vmin]之间。可以在这个区间内二分搜索N个数中的第K大数p

While(Vmax - Vmin > delta){Vmid  = Vmin + (Vmax - Vmin)*0.5;if(f(arr,N,Vmid)  >=  K)Vmin  = Vmid;else Vmax = Vmid;  }

f(arr,N,Vmid) 返回数组arr[0,N - 1]中大于等于Vmid的数的个数,delta要比N个数中,任意最小的两个数的差值小。时间复杂度为ON*log2|Vmax - Vmin|/delta,时间复杂度跟数据分布有关。在数据分布均匀情况下,时间复杂度为ON*log2N)。

JD 题目1534:数组中第K小的数字

#define Max_N 100005#define Max_M 100005typedef long long LL;using namespace std;int N,M;int A[Max_M];int B[Max_N];LL K;bool judge(LL x){    LL sum = 0;    for(int i = 1;i <= M;i++)    {        LL a = A[i];        sum += upper_bound(B+1 , B+1+N , x - a) - (B+1) ;        if(sum >= K) return true;    }    return sum >= K;}LL solve(){    sort(A + 1,A + 1 + M);    sort(B + 1,B + 1 + N);    LL min_ = A[1] + B[1];    LL max_ = A[M] + B[N];    LL mid ,ans = 0;    while(min_ <= max_)    {        mid = (min_ + max_)>>1;        if(judge(mid))        {            ans = mid;            max_ = mid - 1;        }        else            min_ = mid + 1;    }    return ans;}int main(){    while(cin >> M >> N >> K)    {        for(int i = 1;i <= M;i++)            scanf("%d",&A[i]);        for(int i = 1;i <= N;i++)            scanf("%d",&B[i]);        cout << solve() << endl;    }    return 0;}


2. 如果N个数都是正整数,且它们的取值范围不太大,可以考虑申请空间,记录每个正数出现的次数,然后再从大到小取最大的K个。比如,所有正数在(0MAXN)之间,利用一个数组count[MAXN]来记录每个正数出现的个数,count[i]表示正数i出现的个数。只要扫描一遍就可以得到count数组,然后寻找第K大的元素。


 for(sumCount = 0, v = MAXN - 1;v >= 0;v--)    {        sumCount += count[v];        if(sumCount >= K)            break;    }    return v;


三、在N个数中,寻找第KM大的数(0 < K <= M <= N

先找出第Kp和第M大的数q,再扫描一遍,取出[p ,q]之间的数。

1 0
原创粉丝点击