1)划分法
/**
思路:利用快速排序的原理,每次选取第left的值作为参考值:找出一个划分位置low,使得L[low],左边的值比参考值大,右边的值比参考值小,
这样一直持续下去,直到low和K相等,则可以找到前K个最大值。因为选取每个参考值,都要遍历一遍数组,因此:算法复杂度为O(N)。
*/
function partition(L, left, right) {
let low = left;
if (left < right) {
let key = L[left];
let high = right;
while (low < high) {
while (low < high && L[high] <= key) {
high -= 1;
}
L[low] = L[high];
while (low < high && L[low] >= key) {
low += 1;
}
L[high] = L[low];
}
L[low] = key;
return low;
}
}
function topK(L, k) {
let low = 0;
let high = L.length - 1;
let j = partition(L, low, high);
while (j != k && low < high) {
if (k > j) {
low += 1;
}else{
high = j;
}
j = partition(L, low, high);
}
}
let L = [3,2,7,4,6,5,1,8,0, 19, 23, 4, 5, 23, 3, 4, 0,1,2,3,45,6,5,34,212,3234,234,3,4,4,3,43,43,343,34,34,343,43,2]
let n = 10;
topK(L, n);
console.log(L.slice(0, n));
/**
[ 234, 3234, 343, 343, 212, 45, 43, 43, 34, 43 ]
*/
2)大顶堆法
/**
思路:先用前K个值构建大顶堆,也就是顶部是最大值,如果下一个值比顶部大,则立马调整这个大顶堆,
否则取叶子节点肯定是一个最小值,如果数组中值比最小值还小,则直接舍弃。 算法复杂度为O(N * log(N))
*/
function build_heap(lists, size) {
let sz = Math.floor(size / 2);
for (let i = sz - 1; i >= 0; i--) {
adjust_heap(lists, i, size);
}
}
function adjust_heap(lists, i, size) {
let lchild = 2 * i + 1;
let rchild = 2 * i + 2;
let max = i;
if (i < size / 2) {
if (lchild < size && lists[lchild] > lists[max]) {
max = lchild
}
if (rchild < size && lists[rchild] > lists[max]) {
max = rchild;
}
if (max != i) {
let temp = lists[max];
lists[max] = lists[i];
lists[i] = temp;
adjust_heap(lists, max, size);
}
}
}
function heap_sort(lists) {
let size = lists.length;
build_heap(lists, size);
for (let i = size - 1; i >= 0; i--) {
let temp = lists[i];
lists[i] = lists[0];
lists[0] = temp;
adjust_heap(lists, 0, i);
}
return lists;
}
let L = [3, 2, 7, 4, 6, 5, 1, 8, 0, 19, 23, 4, 5, 23, 3, 4, 0, 1, 2, 3, 45, 6, 5, 34, 212, 3234, 234, 3, 4, 4, 3, 43, 43, 343, 34, 34, 343, 43, 2]
let n = 10;
let new_L = heap_sort(L);
console.log(new_L.slice(new_L.length-n, new_L.length).reverse());
/**
[ 3234, 343, 343, 234, 212, 45, 43, 43, 43, 34 ]
*/