N个数里面找出最大的k个数

504 篇文章 0 订阅

转自出处


题目:给出N个无序的数,然后找出其中最大的k个数

解题思路:

         首先测试数据有可能会有一亿个数,数据量特别的大,数据库不可能存储这么多的数据。如果直接sort排序,NlogN时间复杂度实在是太高,大于10^9。我们可以考虑对数据进行分块读取,每次读取的数据块大小应大于k

         不如先假设第一次读取的数据块前k个数最大,然后把k个数建成最小二叉堆。然后从第k+1个数开始,每个数都与堆顶的数值进行比较,如果数字i大于堆顶则把堆顶的元素的元素替换成i,再调整一次堆。最后读取完数据之后,这个二叉堆里面的元素就是从小到大排序好的最大k个数。

时间复杂度:O(NlogK)

空间复杂度:O(K)

证明过程:

         为什么求最大的k个用的不是最大堆,而是最小堆?最大堆堆顶的元素是最大的,往下的子树越来越小,把N个数建成最大堆,那么堆顶往下的k个数就是最大的k个数。但是时间复杂度O(NlogN)和空间复杂度O(N)太高!

         排序时间复杂度很高,是因为进行了很多没有用的判断,我们只需要取最大的k个数,而排序则把N个数都从小到大排序好了。建立一个k个数的最小堆,假设堆里面的元素是最大的,当然只是假设。如果从M+1N这些数只要有数大于最小堆堆顶的数,那么假设就不成立,堆顶那个数就不符合,自然把它去掉,把新的数加进来,再重新调整堆,使得堆顶的元素最小。

         为什么要用最小堆呢?因为每次查找这k个数里面的最小的那个数就是堆顶,时间复杂度是O1)。如果直接用数组来存储这k个数,虽然查找的时间复杂度是logN,但是当把这个数插入数组的时候,数组比它小其他元素还需要往前平移,所以时间复杂度远远大于logN。由于每次调整堆的时间复杂度是logN。所以最小堆的做法的时间复杂度是

O(NlogK),而空间复杂度只有O(K)

 

代码1  C++的STL库优先队列实现二叉堆

[html]  view plain copy
  1. #include <stdio.h>   
  2. #include <stdlib.h>   
  3. #include <string.h>   
  4. #include <algorithm>   
  5. #include <queue>   
  6. using namespace std;   
  7. struct cmp{   
  8.    bool operator ()(int a,int b)   
  9.    {   
  10.        return a>b;   
  11.    }   
  12. };   
  13. #define MAX 11000   
  14. int a[MAX];   
  15. using namespace std;   
  16. priority_queue<int,vector<int>,cmp>q;   
  17. int main()   
  18. {   
  19.    int n,i,k,m,top;   
  20.    scanf("%d%d",&n,&m);   
  21.      for(i=1;i<=n;i++)   
  22.      {   
  23.           scanf("%d",&k);   
  24.           if(i<=m)   //前m个数入 队列   
  25.           {   
  26.                q.push(k);   
  27.                if(i==m)  //纪录前m个数中最小的数   
  28.                     top=q.top();   
  29.           }   
  30.           else   
  31.           {   
  32.                if(k>top)  //如果新加入的数大于队列中最小的数则出队   
  33.                {   
  34.                     q.pop();   
  35.                     q.push(k);   
  36.                     top=q.top();   
  37.                }   
  38.           }   
  39.      }   
  40.      k=0;   
  41.      while(!q.empty())  //这样处理是为了最后一个数打印时没有空格   
  42.      {   
  43.           a[k++]=q.top();   
  44.           q.pop();   
  45.      }   
  46.      for(i=0;i<k;i++)   
  47.      {   
  48.           printf("%d",a[i]);   
  49.           if(i==k-1)   
  50.                printf("\n");   
  51.           else   
  52.                printf(" ");   
  53.      }   
  54.    return 0;   
  55. }  


 

代码2 (数组实现堆):

[html]  view plain copy
  1. #include <stdio.h>   
  2. #define MAX 10001   
  3. int a[MAX];   
  4. void HeapAdjust(int R[],int s,int t)  //筛选函数1   
  5. {   
  6.    int i,j,temp;   
  7.    temp=R[s];   
  8.    i=s;   
  9.    for(j=2*i;j<=t;j=2*j)   
  10.    {   
  11.        if(j<t&&R[j]<R[j+1])   
  12.            j++;   
  13.        if(temp>R[j]) break;   
  14.        R[i]=R[j];   
  15.        i=j;   
  16.    }   
  17.    R[i]=temp;   
  18. }   
  19.   
  20. void HeapSort(int R[],int n)   //堆排   
  21. {   
  22.    int i;   
  23.    for(i=n/2;i>0;i--)   
  24.    {   
  25.        HeapAdjust(R,i,n);   
  26.    }   
  27.    for(i=n;i>1;i--)   
  28.    {   
  29.        R[1]^=R[i];   
  30.        R[i]^=R[1];   
  31.        R[1]^=R[i];   
  32.        HeapAdjust(R,1,i-1);   
  33.    }   
  34. }   
  35.   
  36. void HeapAdjust2(int R[],int s,int t)   //筛选函数2   
  37. {   
  38.    int i,j,temp;   
  39.    temp=R[s];   
  40.    i=s;   
  41.    for(j=2*i;j<=t;j=2*j)   
  42.    {   
  43.        if(j<t&&R[j]>R[j+1])   
  44.            j++;   
  45.        if(temp<R[j]) break;  //找到比新加入的元素还大的根节点   
  46.        R[i]=R[j];   
  47.        i=j;   
  48.    }   
  49.    R[i]=temp;   
  50. }   
  51.   
  52. int main()   
  53. {   
  54.    int i,k,n,m;   
  55.    scanf("%d%d",&n,&m);   
  56.    for(i=1;i<=m;i++)   
  57.    {   
  58.        scanf("%d",&a[i]);   
  59.    }   
  60.    HeapSort(a,m);   
  61.    for(i=m+1;i<=n;i++)   
  62.    {   
  63.        scanf("%d",&k);   
  64.        if(k>a[1])         //新元素大于堆中最小元素则加入堆   
  65.        {   
  66.            a[1]=k;   
  67.            HeapAdjust2(a,1,m);   //从根节点开始重新筛选一次   
  68.        }   
  69.    }   
  70.    HeapSort(a,m);   
  71.    for(i=1;i<=m;i++)   
  72.    {   
  73.        printf("%d",a[i]);   
  74.        if(i==m)   
  75.            printf("\n");   
  76.        else   
  77.            printf(" ");   
  78.    }   
  79.    return 0;   
  80. }  

注:原创文章,转载请注明出处


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值