本文转载自:http://blog.csdn.net/xiaoding133/article/details/8037086

问题:查找大量无序元素中最大的K个数。

解法一:该解法是大部分能想到的,也是第一想到的方法。假设数据量不大,可以先用快速排序或堆排序,他们的平均时间复杂度为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)较小的情况下,选择部分排序。

/*将数组a[s]...a[t]中的元素用一个元素划开,保存中a[k]中*/
void partition(int a[], int s,int t,int &k)
{    int i,j,x;    x=a[s];    //取划分元素     i=s;        //扫描指针初值     j=t;    do    {    while((a[j]<x)&&i<j) j--;   //从右向左扫描,如果是比划分元素小,则不动  if(i<j) a[i++]=a[j];           //大元素向左边移     while((a[i]>=x)&&i<j) i++;      //从左向右扫描,如果是比划分元素大,则不动   if(i<j) a[j--]=a[i];            //小元素向右边移     }while(i<j); //直到指针i与j相等
a[i]=x;      //划分元素就位
k=i;
}
/*查找数组前K个最大的元素,index:返回数组中最大元素中第K个元素的下标(从0开始编号),high为数组最大下标*/
int FindKMax(int a[],int low,int high,int k)
{   int q;
int index=-1;  if(low < high)    {    partition(a , low , high,q);    int len = q - low + 1; //表示第几个位置      if(len == k)    index=q; //返回第k个位置     else if(len < k)     index= FindKMax(a , q + 1 , high , k-len);       else   index=FindKMax(a , low , q - 1, k);    }    return index;  }
int main()
{  int a[]={20,100,4,2,87,9,8,5,46,26};    int Len=sizeof(a)/sizeof(int);   int K=4;  FindKMax(a , 0 , Len- 1 , K) ;      for(int i = 0 ; i < K ; i++)    cout<<a[i]<<" ";    return 0;
}  

解法二:(掌握)避免对前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)。
   代码如下: co

 解法三:(掌握)用容量为K的最小堆来存储最大的K个数。最小堆的堆顶元素就是最大K个数中的最小的一个。每次扫描一个数据X,如果X比堆顶元素Y小,则不需要改变原来的堆。如果X比堆顶元素大,那么用X替换堆顶元素Y,在替换之后,X可能破坏了最小堆的结构,需要调整堆来维持堆的性质。调整过程时间复杂度为O(logK)。 全部的时间复杂度为O(N*logK)。
          这种方法当数据量比较大的时候,比较方便。因为对所有的数据只会遍历一次,第一种方法则会多次遍历数组。 如果所查找的K的数量比较大。可以考虑先求出k` ,然后再求出看k`+1 到 2 * k`之间的数据,然后一次求取。
  
  代码如下:

void heapifymin(int Array[],int i,int size)  {  if(i<size)  {  int left=2*i+1;  int right=2*i+2;  int smallest=i;//假设最小的节点为父结点  //确定三个结点中的最大结点  if(left<size)  {  if(Array[smallest]>Array[left])  smallest=left;  }  if(right<size)  {  if(Array[smallest]>Array[right])  smallest=right;  }  //开始交换父结点和最大的子结点  if(smallest!=i)  {  int temp=Array[smallest];  Array[smallest]=Array[i];  Array[i]=temp;  heapifymin(Array,smallest,size);//对调整的结点做同样的交换  }  }  }  //建堆过程,建立最小堆,从最后一个结点开始调整为最小堆
void min_heapify(int Array[],int size)  {  int i;  for(i=size-1;i>=0;i--)  heapifymin(Array,i,size);  }  //k为需要查找的最大元素个数,size为数组大小,kMax存储k个元素的最小堆
void FindMax(int Array[],int k,int size,int kMax[])
{  for(int i=0;i<k;i++)  kMax[i]=Array[i];  //对kMax中的元素建立最小堆  min_heapify(kMax,k);  printf("最小堆如下所示 : \n");
for(i=0;i<k;i++)
printf("%4d",kMax[i]);
printf("\n");  for(int j=k;j<size;j++)  {  if(Array[j]>kMax[0]) //如果最小堆的堆顶元素,替换  {  int temp=kMax[0];  kMax[0]=Array[j];  Array[j]=temp;  //可能破坏堆结构,调整kMax堆  min_heapify(kMax,k);  }  }  }  int main()
{  int a[]={10,23,8,2,52,35,7,1,12};  int length=sizeof(a)/sizeof(int);  //最大四个元素为23,52,35,12
/***************查找数组中前K个最大的元素****************/  int k=4;  int * kMax=(int *)malloc(k*sizeof(int));  FindMax(a,k,length,kMax);  printf("最大的%d个元素如下所示 : \n",k);  for(int i=0;i<k;i++)  printf("%4d",kMax[i]);  printf("\n");  return 0;
}  

 解法四:这也是寻找N个数中的第K大的数算法。利用二分的方法求取TOP k问题。 首先查找 max 和 min,然后计算出mid = (max + min) / 2该算法的实质是寻找最大的K个数中最小的一个。该算法在实际应用中效果不佳。

 const int N = 8 ;    const int K = 4 ;    /*  利用二分的方法求取TOP k问题。  首先查找 max 和 min,然后计算出 mid = (max + min) / 2  该算法的实质是寻找最大的K个数中最小的一个。   */    int find(int * a , int x) //查询出大于或者等于x的元素个数      {    int sum = 0 ;    for(int i = 0 ; i < N ; i++ )    {     if(a[i] >= x)    sum++ ;                    }    return sum ;    }    int getK(int * a , int max , int min) //最终max min之间只会存在一个或者多个相同的数字      {    while(max - min > 1)             //max - min的值应该保证比两个最小的元素之差要小      {    int mid = (max + min) / 2 ;    int num = find(a , mid) ;    //返回比mid大的数字个数      if(num >= K)                 //最大的k个数目都要比min值大      min = mid ;                   else    max = mid  ;    }    cout<<"end"<<endl;    return min ;    }    int main()    {    int a[N] = {54, 2 ,5 ,11 ,554 ,65 ,33 ,2} ;      int x = getK(a , 554 , 2) ;     cout<<x<<endl ;     getchar() ;     return 0 ;        }  

          解法五:如果N个数都是正数,取值范围不太大,可以考虑用空间换时间。申请一个包括N中最大值的MAXN大小的数组count[MAXN],count[i]表示整数i在所有整数中的个数。这样只要扫描一遍数组,就可以得到第K大的元素。
代码如下:

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

扩展问题:

1.如果需要找出N个数中最大的K个不同的浮点数呢?比如,含有10个浮点数的数组(1.5,1.5,2.5,3.5,3.5,5,0,- 1.5,3.5)中最大的3个不同的浮点数是(5,3.5,2.5)。
     解答:除了解法五不行,其他的都可以。因为最后一种需要是正数。
2. 如果是找第k到第m(0<k<=m<=n)大的数呢?
       解答:可以用小根堆来先求出m个最大的,然后从中输出k到m个。
3. 在搜索引擎中,网络上的每个网页都有“权威性”权重,如page rank。如果我们需要寻找权重最大的K个网页,而网页的权重会不断地更新,那么算法要如何变动以达到快速更新(incremental update)并及时返回权重最大的K个网页?
   解答:(解法三)用堆排序当每一个网页权重更新的时候,更新堆。

举一反三:查找最小的K个元素

解答:最直观的方法是用快速排序或堆排序先排好,在取前K小的数据。最好的办法是利用解法二解法三的原理进行查找。

【编程之美】读书笔记:寻找最大的K个数相关推荐

  1. 编程之美2.5 寻找最大的K个数

    在一个数组中寻找最大的K个数,我们首先说一种非常简单的方法,利用快速排序中的分割算法,即我们经常看见的partition.这个函数会返回一个 int 类型的值,这个值代表的是前一半数字和后一半数字的分 ...

  2. 编程之美 - 读书笔记 - 卖书折扣问题的贪心解法

    <编程之美>读书笔记(四):卖书折扣问题的贪心解法 每 次看完<编程之美>中的问题,想要亲自演算一下或深入思考的时候,都觉得时间过得很快,动辄一两个小时,如果再把代码敲一遍的话 ...

  3. 编程之美 - 读书笔记 - 烙饼问题与搜索树

    前 面已经写了一些关于烙饼问题的简单分析,但因为那天太累有些意犹未尽,今天再充实一些内容那这个问题研究透.我想,通过这篇文章,我们就可以把这一类问题 搞懂.再遇到优化问题,如果我们想不到别的办法,就可 ...

  4. 【编程之美/读书笔记】Chapter 1 游戏之乐

    这里就不写每个问题的题目了,只是记录一下自己的总结和心得. 1.1 让CPU占用率听你指挥 这个题目我刚接触的想法是和多核多线程要扯上关系的,因为自己写个死循环只能跑到CPU 35%左右的占用率,但是 ...

  5. Java并发编程之美读书笔记-并发编程基础2

    2019独角兽企业重金招聘Python工程师标准>>> 1.线程的通知与等待 Java中的Object类是所有类的父亲,鉴于继承机制,Java把所有类都需要的方法放到了Object类 ...

  6. 编程之美读书笔记2.1—求二进制数中1的个数

    解法一: 可以举一个8位二进制的例子.对于二进制操纵,我们除以一个2,原来数字就会减少一个0(向右移一位).如果除的过程中有余,那么久表示当前位置有一个1. 以10100010为例: 第一次除以2时, ...

  7. 编程之美--读书笔记--返回一个数组中所有元素被第一个元素除的结果

    笔试题目1:写一个函数,返回一个数组中所有元素被第一个元素除的结果 很多人会想到如下: void DivAarry(int *pArray,int size) { for(int i=size-1;i ...

  8. 编程之美读书笔记之-高效率的安排见面会

    问题一: n个同学,分别对m个招聘见面会感兴趣.为了满足所有学生的要求,hr希望让每个同学都能参加自己所有感兴趣的见面会.然后每个见面会的时间为t.问如何安排见面会能够使得所有见面会总的时间最短. 建 ...

  9. 中国象棋将帅问题java_编程之美读书笔记1.2——中国象棋将帅问题

    http://blog.csdn.net/pipisorry/article/details/36380669 问题:下过中国象棋的朋友都知道,双方的"将"和"帅&quo ...

  10. 编程之美读书笔记_1.1_让CPU占用率曲线听你指挥

    题目:写一个程序,让用户来决定Windows任务管理器(Task Manager)的CPU占用率.程序越精简越好,计算机语言不限.例如,可以实现下面三种情况:   1.    CPU的占用率固定在50 ...

最新文章

  1. FPGA之道(68)原语的使用
  2. Centos7 系统下搭建.NET Core2.0+Nginx+Supervisor+Mysql环境
  3. HDU-1857 畅通工程再续
  4. 持续集成之 Spring Boot 实战篇
  5. Ubuntu设置静态IP/网关
  6. 目前自己的几种脱壳方式
  7. JavaWeb POI 导出Excel
  8. 微软官方office教程和微软官方office模板
  9. 【深度解读】电动自行车新国标对锂电池行业的影响分析
  10. v-for渲染img标签图片
  11. rabbitmq User can only log in via localhost
  12. ps无法完成请求因为程序错误
  13. 多版本谷歌chrome浏览器并存(谷歌浏览器从C盘移植)
  14. 【Python】只需2行代码,轻松将PDF转换成Word(含示范案例)
  15. 《开源合辑-(游戏/娱乐-角色扮演)之(Java)》
  16. ArabellaCPC 2019 B. Road to Arabella
  17. 飞思卡尔MC9S12系列单片机地址影射以及分页问题
  18. 将一个数组中的值按逆序重新存放。例如,原来的顺序为8,6,5,4,1。要求改为1,4,5,6,8。输出逆序后数组的整数,每两个整数之间用空格分隔。
  19. - java实现审核流程
  20. MacOS装机必备应用下载合集(2019年07月更新)

热门文章

  1. 联盟营销最佳实践:提高联盟计划的投资回报率
  2. php判断百度蜘蛛ip,判断百度蜘蛛偷偷进行301转移权重 给新站提权
  3. disabled与enabled是什么?disabled与enabled属性详解
  4. 嵌入式开发培训靠谱吗,嵌入式开发培训怎么样?
  5. 香港流行乐黄金二十年——经典歌手(音乐人)全面回顾 二
  6. Vonage与华尔街英语合作为其数字教室提供助力
  7. arcgis 发布服务
  8. Error Based Injection和sql注入函数
  9. 计算机编程语言排行榜—TIOBE世界编程语言排行榜(2021年11月份最新版)
  10. 基于[三星6818]芯片超声波测距驱动编写