查找无序数中最大k个数

来源:互联网 发布:ubuntu如何安装win10 编辑:程序博客网 时间:2024/05/21 06:52
解法一:该解法是大部分能想到的,也是第一想到的方法。假设数据量不大,可以先用快速排序或堆排序,他们的平均时间复杂度为O(N*logN),然后取出前K个,时间复杂度为O(K),总的时间复杂度为O(N*logN)+O(K).
        当K=1时,上面的算法的时间复杂度也是O(N*logN),上面的算法是把整个数组都进行了排序,而原题目只要求最大的K个数,并不需要前K个数有限,也不需要后N-K个数有序。可以通过部分排序算法如选择排序和交换排序,把N个数中的前K个数排序出来,复杂度为O(N*K),选择哪一个,取决于K的大小,在K(K<logN)较小的情况下,选择部分排序。
解法二:(掌握)避免对前K个数进行排序来获取更好的性能(利用快速排序的原理)。
        假设N个数存储在数组S中,从数组中随机找一个元素X,将数组分成两部分Sa和Sb.Sa中的元素大于等于X,Sb中的元素小于X。
    出现如下两种情况:
   (1)若Sa组的个数大于或等于K,则继续在sa分组中找取最大的K个数字 。
   (2)若Sa组中的数字小于K ,其个数为T,则继续在sb中找取 K-T个数字 。
   一直这样递归下去,不断把问题分解成小问题,平均时间复杂度为O(N*logK)。
   代码如下:
  1. void partition(int a[], int s,int t,int &k) 

  2. int i,j,x; 
  3. x=a[s]; //取划分元素 
  4. i=s; //扫描指针初值 
  5. j=t; 
  6. do 

  7. while((a[j]<x)&&i<j) j--; //从右向左扫描,如果是比划分元素小,则不动
  8. if(i<j) a[i++]=a[j]; //大元素向左边移 
  9. while((a[i]>=x)&&i<j) i++; //从左向右扫描,如果是比划分元素大,则不动 
  10. if(i<j) a[j--]=a[i]; //小元素向右边移 

  11. }while(i<j); //直到指针i与j相等 
  12. a[i]=x; //划分元素就位 
  13. k=i; 

  14. /*查找数组前K个最大的元素,index:返回数组中最大元素中第K个元素的下标(从0开始编号),high为数组最大下标*/
  15. int FindKMax(int a[],int low,int high,int k)

  16. int q;
  17. int index=-1;
  18. if(low < high) 

  19. partition(a , low , high,q); 
  20. int len = q - low + 1; //表示第几个位置 
  21. if(len == k) 
  22. index=q; //返回第k个位置 
  23. else if(len < k) 
  24. index= FindKMax(a , q + 1 , high , k-len); 
  25. else 
  26. index=FindKMax(a , low , q - 1, k); 

  27. return index;

  28. }
  29. int main()
  30. {
  31. int a[]={20,100,4,2,87,9,8,5,46,26}; 
  32. int Len=sizeof(a)/sizeof(int); 
  33. int K=4;
  34. FindKMax(a , 0 , Len- 1 , K) ; 
  35. for(int i = 0 ; i < K ; i++) 
  36. cout<<a[i]<<" "; 
  37. return 0;
  38. }
复制代码
解法三:(掌握)用容量为K的最小堆来存储最大的K个数。最小堆的堆顶元素就是最大K个数中的最小的一个。每次扫描一个数据X,如果X比堆顶元素Y小,则不需要改变原来的堆。如果X比堆顶元素大,那么用X替换堆顶元素Y,在替换之后,X可能破坏了最小堆的结构,需要调整堆来维持堆的性质。调整过程时间复杂度为O(logK)。 全部的时间复杂度为O(N*logK)。
          这种方法当数据量比较大的时候,比较方便。因为对所有的数据只会遍历一次,第一种方法则会多次遍历数组。 如果所查找的K的数量比较大。可以考虑先求出k` ,然后再求出看k`+1 到 2 * k`之间的数据,然后一次求取。
  1. void heapifymin(int Array[],int i,int size)
  2. {
  3. if(i<size)
  4. {
  5. int left=2*i+1;
  6. int right=2*i+2;
  7. int smallest=i;//假设最小的节点为父结点
  8. //确定三个结点中的最大结点
  9. if(left<size)
  10. {
  11. if(Array[smallest]>Array[left])
  12. smallest=left;
  13. }
  14. if(right<size)
  15. {
  16. if(Array[smallest]>Array[right])
  17. smallest=right;
  18. }

  19. //开始交换父结点和最大的子结点
  20. if(smallest!=i)
  21. {
  22. int temp=Array[smallest];
  23. Array[smallest]=Array[i];
  24. Array[i]=temp;
  25. heapifymin(Array,smallest,size);//对调整的结点做同样的交换
  26. }
  27. }
  28. }

  29. //建堆过程,建立最小堆,从最后一个结点开始调整为最小堆
  30. void min_heapify(int Array[],int size)
  31. {
  32. int i;
  33. for(i=size-1;i>=0;i--)
  34. heapifymin(Array,i,size);

  35. }
  36. //k为需要查找的最大元素个数,size为数组大小,kMax存储k个元素的最小堆
  37. void FindMax(int Array[],int k,int size,int kMax[])
  38. {

  39. for(int i=0;i<k;i++)
  40. kMax[i]=Array[i];
  41. //对kMax中的元素建立最小堆
  42. min_heapify(kMax,k);
  43. printf("最小堆如下所示 : \n");
  44. for(i=0;i<k;i++)
  45. printf("%4d",kMax[i]);
  46. printf("\n");

  47. for(int j=k;j<size;j++)
  48. {
  49. if(Array[j]>kMax[0]) //如果最小堆的堆顶元素,替换
  50. {
  51. int temp=kMax[0];
  52. kMax[0]=Array[j];
  53. Array[j]=temp;
  54. //可能破坏堆结构,调整kMax堆
  55. min_heapify(kMax,k);
  56. }


  57. }



  58. }

  59. int main()
  60. {

  61. int a[]={10,23,8,2,52,35,7,1,12};
  62. int length=sizeof(a)/sizeof(int);

  63. //最大四个元素为23,52,35,12
  64. /***************查找数组中前K个最大的元素****************/
  65. int k=4;
  66. int * kMax=(int *)malloc(k*sizeof(int));
  67. FindMax(a,k,length,kMax);

  68. printf("最大的%d个元素如下所示 : \n",k);
  69. for(int i=0;i<k;i++)
  70. printf("%4d",kMax[i]);
  71. printf("\n");
  72. return 0;
  73. }
复制代码
0 0