寻找最大的K个数

前提条件:有N个无序的数,假定它们各不相等,如何选出其中最大的若干个数

解法一:

适用于元素数量不多,内存中可存储整个数组序列。通过快速排序或堆排序对数组排序,时间复杂度为O(N*log2N),然后取出前K个数,时间复杂度为O(K),总时间复杂度为O(N*log2N) + O(K),进一步的,可以知道,我们只需要前 K 大的数,所以并不需要对前 K 个数排序,也不需要对后 N - K 个数排序,这样,通过部分排序的思想,可以选用选择排序,选出前 K 大的值,总时间复杂度为O(N*K),至于选用哪种方式,根据 K 的大小进行选择。

解法二:

适用于元素数量不多,内存中可存储整个数组序列。根据快速排序的思想,每一步中都是将待排序数据分做两组,其中一组数据的任一元素都比另一组数据的任一元素大或者小,因此在本问题中,可以考虑,从数组 S 中随机选出一个元素 X ,把数组分为两部分 Sa 和 Sb,Sa 的元素大于等于 X,Sb的元素小于X。这时,有两种可能性:

  • Sa 中元素的个数小于K,Sa 中所有的数和 Sb 中最大的 K - |Sa| 个元素
  • Sa 中原色的个数大于或等于 K,则需要返回 Sa 中最大的 K 个元素

时间复杂度为O(N*log2K)。

解法三:

适用于元素数量不多,内存中可存储整个数组序列。寻找 N 个数中最大的 K 个数,其实就是要找出 K 个数中最小的那个,那么可以通过二分测探的方式,在这 N 个数所在的区间进行探测。假设 N 个数中最大的是 Vmax,最小的是 Vmin,那么这 N 个数中的第 K 大数就在区间 [Vmin,Vmax]之间,在这个区间二分搜索第 K 大的数。时间复杂度为 O(N*log2N)。

解法四:

 维护一个 K 大小的堆,当前遍历的元素比堆中最小元素大,那么就更新这个堆,最后堆中的 K 个元素就是最大的 K 个数。时间复杂度为O(N*log2K)。这种方法适用于内存无法存储所有的 N 个数。一般情况下,内存中都可以维护一个 K 大小的堆,但是当 K 也特别大,以至于内存中无法存储 K 大小的堆的时候,可以尝试找出 K‘ 个元素,然后再找出 K' + 1 到 2K' 的元素,这样,需要遍历全部元素 K / K’ 趟。

解法五:

如果所有的元素都是正整数,且其范围不大,那么可通过计数排序的思想,来获得前 K 大的值。比如所有整数都在 [0,MAX] 区间,那么通过一个 cnt[MAX] 数组记录元素出现的次数,然后从大到小取出 K 个最大的元素。在实际情况,往往并不能保证所有数都是正整数,且元素都是小范围,那么当元素有正有负,其其范围涵盖较广的情况,可以将元素所在区间 [Vmin,Vmax] 分成M块,每个小区间跨度在 (Vmax - Vmin)/ M,统计各个小区间内元素的个数,可知前 K 大的数在哪个区间,然后再对那个区间进行处理,对小区间处理方式就可参考前面所述的几种方法了。

写在最后

以上参考自 《编程之美》

 

posted @ 2018-03-30 13:24  zxzhang  阅读(1163)  评论(0编辑  收藏  举报