构造堆
看到这个题目,找第k个大的元素,第一想法是堆排序!把数组按照堆排序的方式排列,然后进行k次移除最大的元素操作,就能得到结果。
/**
* @param {number[]} nums
* @param {number} k
* @return {number}
*/
var arr;
var size;
var findKthLargest = function(nums, k) {
//buildHeap
var result;
arr = nums;
size = arr.length;
for(let i=Math.floor(size/2)- 1;i>=0;i--){
siftdown(i);
}
for(let i=0;i<k;i++){
result = removeMax();
}
return result;
};
function siftdown(i){
if(i > Math.floor(size/2)- 1) return;
let child = 2 * i + 1;
if(child < size-1){
if(arr[child] < arr[child+1])
child++;
}
if(arr[child] > arr[i]){
swap(child,i);
siftdown(child);
}
}
function swap(i,j){
let temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}v
function removeMax(){
swap(0,size-1);
size--;
siftdown(0);
return arr[size];
}
确实,这个思路能通过。 时间复杂度应该是O(n + klogn)=O(nlogn),空间复杂度是O(logn)。堆排序也确实是个不错的方法,但用堆排序有没有更优雅的方式呢? 当然是有的! 我们可以建一个大小为k的最小堆,来记录数组最大的k个元素。通过扫描数组一遍,不断更新调整这个最小堆,最后堆顶的元素,就是我们要求的结果:
var findKthLargest = function (nums, k) {
var heap = nums.slice(0,k);
var siftdown = (i) => {
if(i > Math.floor(k/2)) return;
let child = 2 * i + 1;
if(child < heap.length-1 && heap[child+1] < heap[child]){
child++;
}
if(heap[child] < heap[i]){
swap(heap,i,child);
siftdown(child);
}
}
for(let i=Math.floor(k/2);i>=0;i--){
siftdown(i);
}
for(let i=k;i<nums.length;i++){
if(nums[i] > heap[0]){
heap[0] = nums[i];
siftdown(0);
}
}
return heap[0];
};
function swap(nums, i, j){
let temp = nums[j];
nums[j] = nums[i];
nums[i] = temp;
}
这个解法对比起对整个数组进行堆排序,用的堆空间更小了,空间复杂度是O(k),时间复杂度是O(nlogk)。
基于快速排序的思路
除了利用堆的思路,我们还可以基于快速排序来减治。 随机选取一个轴值,通过一轮快速排序的交换,获取到这个轴值的位置,如果刚好是nums.length - k,我们就找到了第k大的元素,这是最好的情况。当然,如果是一般情况,我们也减小了问题的规模。 我们令目标值target = nums.length - k,若轴值的位置p<target,我们进一步在p的右边寻找第k大的元素;否则,我们在p的左边寻找。
var findKthLargest = function (nums, k) {
let target = nums.length - k;
let left = 0;
let right = nums.length - 1;
while(true){
let p = partition(nums,left,right);
if(p === target) return nums[p];
else if(p < target) left = p + 1;
else right = p - 1;
}
};
function partition(nums, left, right){
let p = nums[left];
let j = left;
for(let i = left+1;i<=right;i++){
if(nums[i] < p){
j++;
swap(nums,j,i);
}
}
swap(nums,left,j);
return j;
}
function swap(nums,i,j){
let temp = nums[i];
nums[i] = nums[j];
nums[j] = temp;
}
时间复杂度:O(n),空间复杂度:O(1)