zoukankan      html  css  js  c++  java
  • 算法题004 寻找最大的K个数

    寻找最大的K个数

     

    题目描述

      有很多个无序的数,怎么选出其中最大的若干个数?

      即,从n个数中选出最大的K个数。

     

    解法一

      先假设元素的数量不大,例如在几千个左右,在这种情况下,我们就排序吧。

      在这里,快速排序或堆排序都是不错的选择,他们的平均时间复杂度都是O(nlog2n),然后取出钱K个,O(K)。

      总的时间复杂度仍然是O(nlog2n)

      可以注意到,即便是K=1的情况,上面的算法复杂度仍然是O(nlog2n),而显然,我们可以通过n-1此的比较和交换得到结果,不需要对整个数组进行排序。

      要避免做后面n-K个数的排序,可以使用部分排序算法,选择排序和冒泡排序都是不错的选择。

      把n个数中的前K个数排序出来,复杂度是O(n*K)

      哪一个更好呢?O(nlog2n) 和O(n*K)这取决于K的大小,在K<log2n的情况下,可以选择部分排序。

     

    解法二

      回忆一下快速排序,快排中的每一步,都是将数据分为两组,其中一组的任何数都小于另一组中的任何数,不断地对数据进行分割直到不能再分即完成排序。

      假设n个数存储在数组S中,从S中找出一个元素X,它把数组分为比它大的和比它小的两部分,假设比它大的一部分数组是Smax,比它小的部分是Smin。

      这时有两种可能:

      1.Smax中元素不足K个,说明Smax中的所有数和Smin中最大的K-|Smax|个元素就是数组S中最大的K个数;

      2.Smax中元素的个数大于或等于K,则需要返回Smax中最大的K个元素。

      这样递归下去,问题的规模不断地变小,平均时间复杂度O(n * log2K)

     

    解法三

      寻找n个数中最大的K个数,本质上就是寻找这K个数中最小的那个,也就是第K大的数。

      可以使用二分搜索的策略。对于一个给定的数p,可以在O(n)的时间复杂度内找出所有不小于p的数。

      (此方法详述部分略)。

     

    解法四:如果N非常非常大呢?

      前面三个解法都需要对数据访问多次,如果n很大呢?100亿?这个时候数据不能全部装入内存,所以要求尽可能少地遍历所有数据。

      前K个数中最大的K个数是一个退化的情况,所有K个数就是最大的K个数,如果考虑第K+1个数,则它和前面K个数的最小值进行比较,比其大则替换它。

      如果用一个数组来存储最大的K个数,每加入一个数X,就扫描一遍数组,得到数组中最小的数Y。X和Y进行比较,替换它或者保持原数组不变。这种方法,所耗费的时间复杂度为O(n * K)

      进一步,可以用容量为K的最小堆来存储最大的K个数。最小堆的堆顶元素就是最大K个数中最小的一个。每次新考虑一个数X,如果X小于堆顶则舍弃,如果X大于堆顶,那么用X替换堆顶,然后更新堆来维持堆的性质。(因为X可能并不是最小值,所以堆结构需要更新)。更新过程花费的时间复杂度为O(log2K)。

      因此,算法的时间复杂度为O(n * log2K),这实际上是部分执行了堆排序的算法。

     

    解法五

      如果所有n个数都是整数,且它们的取值范围不太大,可以考虑申请空间,记录每个整数出现的次数,然后再从大到小取最大的K个。

      比如用数组count,count[i]表示整数i出现的次数。

      极端情况下,如果n个整数各不相同,只需要一个bit来存储这个整数是否存在。

      实际情况下,并不一定能保证所有元素都是正整数,且取值范围不太大。但是这种方法仍然可以推广适用。比如把取值区间分成多块,然后统计各个小区间中元素的个数。可以知道第K大的元素在哪一个小区间,然后再对那个小区间继续进行分块处理。

     

    参考资料

      《编程之美》2.5节。

      本题目其他相关链接:

      链接1:http://blog.csdn.net/koala002/article/details/6415485

      链接2:http://www.cnblogs.com/luxiaoxun/archive/2012/08/06/2624799.html

      本博客关于排序算法的回顾:

      排序算法:http://www.cnblogs.com/mengdd/archive/2012/11/24/2786346.html

     

     

  • 相关阅读:
    火狐插件火狐黑客插件将Firefox变成黑客工具的七个插件
    memcache安装环境:WINDOWS 7
    PHP正则表达式
    968. 监控二叉树 力扣(困难) dfs 官方说DP
    375. 猜数字大小 II 力扣(中等) 区间动态规划、记忆化搜索
    629. K个逆序对数组 力扣(困难) 区间动态规划
    剑指 Offer 51. 数组中的逆序对 力扣(困难) 巧用归并排序算法
    488. 祖玛游戏 力扣(困难) dfs
    16. 最接近的三数之和 力扣(中等) 双指针
    319. 灯泡开关 力扣(中等) 数论
  • 原文地址:https://www.cnblogs.com/mengdd/p/2954914.html
Copyright © 2011-2022 走看看