数组中的第K个最大元素
- https://leetcode.cn/problems/kth-largest-element-in-an-array/
描述
- 给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。
- 请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。
- 你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。
示例 1
输入: [3,2,1,5,6,4], k = 2
输出: 5
示例 2
输入: [3,2,3,1,2,4,5,5,6], k = 4
输出: 4
提示
- 1 <= k <= nums.length <= 1 0 5 10^5 105
- - 1 0 4 10^4 104 <= nums[i] <= 1 0 4 10^4 104
算法实现
1 )基于js中原生sort api
const findKthLargest = function(nums, k) {
return nums.sort((a,b) => b-a)[k - 1]
};
- 这个浏览器默认提供的sort()方法,一般时间复杂度是 O(nlogn)
2 )基于堆的数据结构和堆排序的方法
// 建立最小堆类
class MinHeap {
heap: number[] = [];
// 交换节点位置
swap(i, j) {
[this.heap[i], this.heap[j]] = [this.heap[j], this.heap[i]];
}
// 获得父节点
getParentIndex(i) {
return (i - 1) >> 1;
}
// 获取左子节点
getLeftIndex(i) {
return (i << 1) + 1; // 极客写法
}
// 获取右子节点
getRightIndex(i) {
return (i << 1) + 2;
}
// 向上移动
shiftUp(index) {
// 如果到了堆顶元素,index是0,则不要再上移了
if(!index) return;
const parentIndex = this.getParentIndex(index)
if(this.heap[parentIndex] > this.heap[index]) {
this.swap(parentIndex, index)
this.shiftUp(parentIndex)
}
}
// 下移
shiftDown(index) {
// 边界1:如果到了堆尾元素,则不要再下移了
if(index >= this.heap.length - 1) return;
const size = this.size();
const leftIndex = this.getLeftIndex(index);
const rightIndex = this.getRightIndex(index);
if (leftIndex < size && this.heap[leftIndex] < this.heap[index]) {
this.swap(leftIndex, index);
this.shiftDown(leftIndex);
}
if (rightIndex < size && this.heap[rightIndex] < this.heap[index]) {
this.swap(rightIndex, index);
this.shiftDown(rightIndex);
}
}
// 插入
insert(value) {
this.heap.push(value);
this.shiftUp(this.heap.length - 1);
}
// 删除堆顶
pop() {
// pop()方法删除数组最后一个元素并返回,赋值给堆顶
this.heap[0] = this.heap.pop();
// 对堆顶重新排序
this.shiftDown(0);
}
// 获取堆顶
peak() {
return this.heap[0];
}
// 获取堆的大小
size() {
return this.heap.length;
}
}
// 实现
const findKthLargest = (nums, k) => {
const h = new MinHeap();
nums.forEach(n => {
// 将数组元素依次插入堆中
h.insert(n);
// 如果堆满,则执行优胜劣汰
(h.size() > k) && h.pop();
})
// 返回堆顶,此时就是第k大的元素
return h.peak();
};
- 关键在于这个堆的数据结构提供的 insert 方法 与 pop 方法
- 时间复杂度:O(nlogk)
- 一个n循环,里面还嵌套一个heap的上移递归操作logk
- 总体:n*logk
- 空间复杂度: O(k) 或 O(logn)
- 堆的大小,数组的大小, k是输入的堆大小
- 注意
- 本题使用的是一个堆排序的算法,O(nlogn)
- 但是还有其他排序也可以达到这个效率
- 但是这个不符合题目的要求:时间复杂度为 O(n) 的算法
3 )基于冒泡排序文章来源:https://www.toymoban.com/news/detail-729641.html
function findKthLargest(nums: number[], k: number): number {
const len = nums.length - 1;
for (let i = len; i > len - k; i--) {
for (let j = 0; j < i; j++) {
if (nums[j] > nums[j + 1]) {
[nums[j], nums[j + 1]] = [nums[j + 1], nums[j]];
}
}
}
return nums[len - k + 1];
};
- 这个算法基于冒泡排序修改而来,但是无法通过 leetcode , 对于一些用例数据,超出时间限制
4 )基于快速排序文章来源地址https://www.toymoban.com/news/detail-729641.html
function quickselect(nums: number[], l: number, r: number, k: number) {
if (l == r) return nums[k];
const pivot = nums[l];
let i:number = l - 1, j:number = r + 1;
while (i < j) {
do i++; while (nums[i] < pivot);
do j--; while (nums[j] > pivot);
if (i < j) [nums[i], nums[j]] = [nums[j], nums[i]];
}
return k <= j ? quickselect(nums, l, j, k) : quickselect(nums, j + 1, r, k);
}
function findKthLargest(nums: number[], k: number): number {
const n = nums.length;
return quickselect(nums, 0, n - 1, n - k);
}
- 这是官方提供的方法
- 时间复杂度 O(n), 证明过程可以参考「《算法导论》9.2:期望为线性的选择算法」。
- 空间复杂度 O(logn),递归使用栈空间的空间代价的期望为 O(logn)O(\log n)O(logn)。
到了这里,关于数据结构与算法之堆: Leetcode 215. 数组中的第K个最大元素 (Typescript版)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!