寻找最大的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 大的数在哪个区间,然后再对那个区间进行处理,对小区间处理方式就可参考前面所述的几种方法了。
写在最后
以上参考自 《编程之美》
┆ 凉 ┆ 暖 ┆ 降 ┆ 等 ┆ 幸 ┆ 我 ┆ 我 ┆ 里 ┆ 将 ┆ ┆ 可 ┆ 有 ┆ 谦 ┆ 戮 ┆ 那 ┆ ┆ 大 ┆ ┆ 始 ┆ 然 ┆
┆ 薄 ┆ 一 ┆ 临 ┆ 你 ┆ 的 ┆ 还 ┆ 没 ┆ ┆ 来 ┆ ┆ 是 ┆ 来 ┆ 逊 ┆ 没 ┆ 些 ┆ ┆ 雁 ┆ ┆ 终 ┆ 而 ┆
┆ ┆ 暖 ┆ ┆ 如 ┆ 地 ┆ 站 ┆ 有 ┆ ┆ 也 ┆ ┆ 我 ┆ ┆ 的 ┆ 有 ┆ 精 ┆ ┆ 也 ┆ ┆ 没 ┆ 你 ┆
┆ ┆ 这 ┆ ┆ 试 ┆ 方 ┆ 在 ┆ 逃 ┆ ┆ 会 ┆ ┆ 在 ┆ ┆ 清 ┆ 来 ┆ 准 ┆ ┆ 没 ┆ ┆ 有 ┆ 没 ┆
┆ ┆ 生 ┆ ┆ 探 ┆ ┆ 最 ┆ 避 ┆ ┆ 在 ┆ ┆ 这 ┆ ┆ 晨 ┆ ┆ 的 ┆ ┆ 有 ┆ ┆ 来 ┆ 有 ┆
┆ ┆ 之 ┆ ┆ 般 ┆ ┆ 不 ┆ ┆ ┆ 这 ┆ ┆ 里 ┆ ┆ 没 ┆ ┆ 杀 ┆ ┆ 来 ┆ ┆ ┆ 来 ┆