写一个函数maxk(A, k)找到一个数组中最大的k个数字。 如:
maxk([3,5,8,2,1,6],2) // 8, 6 或者 6,8 (结果不要求顺序)maxk([3,5,8,2,1,6],3) // 8,6,5
方案1,虽然简单,但是速度较慢 O(nlgn)
function maxk(A, k) {return A.sort( (a, b) => b - a ).slice(0, k)}
方案2: 利用快速排序的性质,k较小时O(n)
利用快速排序的方法,利用中心点将数组分段的性质,不断缩小范围。
经过一次partition, 数组拆被拆分成3段,中心位置为r:
k = hi - r ,那么刚刚好位置r向右就是最大的k个值
k < hi - r , 那么r向右包含最大的k个值,可以缩小查找范围
k > hi - r , r向右的值不足k个,需要补充, 那么递归查找将 k-> k - (hi - r), hi = r
function swap(A, i, j){[A[i],A[j]] = [A[j], A[i]]}function partition(A, lo, hi) {const pivot = A[hi - 1]let i = lo, j = hi - 1while(i !== j) {A[i] <= pivot ? i++ : swap(A, i, --j)}swap(A, j, hi-1)return j}function maxk(A, k, lo = 0, hi = A.length) {if (k > 0 && hi - lo > 0) {const r = partition(A, lo, hi)if (k === hi - r) { // 最右边k个元素是最大值return A.slice(r)}else if (k > hi - r) { // 右边的元素个数不足k个,需要从左边补充 k - (hi - r - 1)个return maxk(A, k - (hi - r), lo, r)}else { // 右边元素大于k个,缩小范围return maxk(A, k, r + 1, hi)}}return null}
其他方法:
利用冒泡排序,k较大时O(n^2)
利用堆(Heap)这个还没有讲到,k较小时O(n)
