写一个函数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 - 1
while(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)