写一个函数maxk(A, k)找到一个数组中最大的k个数字。 如:

    1. maxk([3,5,8,2,1,6],2) // 8, 6 或者 6,8 (结果不要求顺序)
    2. maxk([3,5,8,2,1,6],3) // 8,6,5

    方案1,虽然简单,但是速度较慢 O(nlgn)

    1. function maxk(A, k) {
    2. return A.sort( (a, b) => b - a ).slice(0, k)
    3. }

    方案2: 利用快速排序的性质,k较小时O(n)

    利用快速排序的方法,利用中心点将数组分段的性质,不断缩小范围。

    经过一次partition, 数组拆被拆分成3段,中心位置为r:

    1. k = hi - r ,那么刚刚好位置r向右就是最大的k个值

    2. k < hi - r , 那么r向右包含最大的k个值,可以缩小查找范围

    3. k > hi - r , r向右的值不足k个,需要补充, 那么递归查找将 k-> k - (hi - r), hi = r

    1. function swap(A, i, j){
    2. [A[i],A[j]] = [A[j], A[i]]
    3. }
    4. function partition(A, lo, hi) {
    5. const pivot = A[hi - 1]
    6. let i = lo, j = hi - 1
    7. while(i !== j) {
    8. A[i] <= pivot ? i++ : swap(A, i, --j)
    9. }
    10. swap(A, j, hi-1)
    11. return j
    12. }
    13. function maxk(A, k, lo = 0, hi = A.length) {
    14. if (k > 0 && hi - lo > 0) {
    15. const r = partition(A, lo, hi)
    16. if (k === hi - r) { // 最右边k个元素是最大值
    17. return A.slice(r)
    18. }
    19. else if (k > hi - r) { // 右边的元素个数不足k个,需要从左边补充 k - (hi - r - 1)个
    20. return maxk(A, k - (hi - r), lo, r)
    21. }
    22. else { // 右边元素大于k个,缩小范围
    23. return maxk(A, k, r + 1, hi)
    24. }
    25. }
    26. return null
    27. }

    其他方法:

    1. 利用冒泡排序,k较大时O(n^2)

    2. 利用堆(Heap)这个还没有讲到,k较小时O(n)