其实刷到这种类型题的时候,我刚开始确实不知道怎么才是一样比较快的解决方案。在刷完题之后才会知道一种优先队列
的方案,可以做我的刷题方案。那么这里对于队列和优先队列不在赘述,分享一篇介绍的不错的文章。
这里只说两个核心的操作:
- insert: 即将元素插入到优先队列中(入队)
- deleteMin(删除最小者) ,它的作用是找出、删除优先队列中的最小的元素(出队),我下面会定义为 pop。
优先队列(堆)的基本实现
最开始的实现:
class MinHeap {
constructor() {
this.heap = [];
}
}
交换节点位置
swap(i1, i2) {
[this.heap[i1], this.heap[i2]] = [this.heap[i2], this.heap[i1]];
}
寻找父节点的位置
//父节点index
getFatherIndex(index) {
return (index - 1) >> 1
}
寻找左子树节点的位置
// 左子树index
getLeftIndex(index) {
return (index * 2) + 1;
}
寻找右子树节点的位置
// 右子树
getRightIndex(index) {
return (index * 2) + 2;
}
上浮: 这里就是一种比较方式, 我们需要对插入底部的元素依次比较重新建堆
shiftUp(index) {
// 首先会判断一下 边界条件
if (index === 0) return;
// 拿到父节点
let fatherIndex = this.getFatherIndex(index);
if (this.heap[fatherIndex] && this.heap[fatherIndex].val > this.heap[index].val) {
this.swap(fatherIndex, index); // 可以画图解决
// 继续
this.shiftUp(fatherIndex);
}
}
下沉: 每次在堆顶取走一个元素之后, 需要从尾部拿来一个元素重新比较建堆
shiftDown(index) {
let leftIndex = this.getLeftIndex(index);
let rightIndex = this.getRightIndex(index);
// 左
if (this.heap[leftIndex] && this.heap[leftIndex].val < this.heap[index].val) {
this.swap(leftIndex, index);
// 继续
this.shiftDown(leftIndex)
}
// 右
if (this.heap[rightIndex] && this.heap[rightIndex].val < this.heap[index].val) {
this.swap(rightIndex, index);
// 继续
this.shiftDown(rightIndex)
}
}
插入元素:
insert(value) {
this.heap.push(value);
// this.shiftUp(this.heap[this.heap.length-1]) 填索引
this.shiftUp(this.heap.length-1);
}
删除元素:
pop() {
// 特殊情况处理 直接优化了
if (this.size() === 1) return this.heap.shift();
let top = this.heap[0];
// 这里是 需要重新调整 忘记了
this.heap[0] = this.heap.pop()
this.shiftDown(0);
return top;
}
取堆顶元素:
peek() {
return this.heap[0]
}
获取堆的长度
size() {
return this.heap.length;
}
这里完整的组合起来就是我们想要的小根堆
,这里是依据链表结构建的堆,使用需要做一些修改
// 这里小根堆为例
class MinHeap {
constructor() {
this.heap = [];
}
swap(i1, i2) {
[this.heap[i1], this.heap[i2]] = [this.heap[i2], this.heap[i1]];
}
//父节点index
getFatherIndex(index) {
return (index - 1) >> 1
}
// 左子树index
getLeftIndex(index) {
return (index * 2) + 1;
}
// 右子树
getRightIndex(index) {
return (index * 2) + 2;
}
// 上浮
shiftUp(index) {
// 首先会判断一下 边界条件
if (index === 0) return;
// 拿到父节点
let fatherIndex = this.getFatherIndex(index);
if (this.heap[fatherIndex] && this.heap[fatherIndex].val > this.heap[index].val) {
this.swap(fatherIndex, index); // 可以画图解决
// 继续
this.shiftUp(fatherIndex);
}
}
// 下沉
shiftDown(index) {
let leftIndex = this.getLeftIndex(index);
let rightIndex = this.getRightIndex(index);
// 左
if (this.heap[leftIndex] && this.heap[leftIndex].val < this.heap[index].val) {
this.swap(leftIndex, index);
// 继续
this.shiftDown(leftIndex)
}
// 右
if (this.heap[rightIndex] && this.heap[rightIndex].val < this.heap[index].val) {
swap(rightIndex, index);
// 继续
this.shiftDown(rightIndex)
}
}
// 插入
insert(value) {
this.heap.push(value);
// this.shiftUp(this.heap[this.heap.length-1]) 填索引
this.shiftUp(this.heap.length-1);
}
// 删除
pop() {
// 特殊情况处理 直接优化了
if (this.size() === 1) return this.heap.shift();
let top = this.heap[0];
// 这里是 需要重新调整 忘记了
this.heap[0] = this.heap.pop()
this.shiftDown(0);
return top;
}
peek() {
return this.heap[0]
}
size() {
return this.heap.length;
}
}
703. 数据流中的第 k 大个元素
代码实现:
class MinHeap {
constructor() {
this.heap = [];
}
getFatherIndex(index) {
return (index - 1) >> 1;
}
getLeftIndex(index) {
return index * 2 + 1;
}
getRightIndex(index) {
return index * 2 + 2;
}
swap(i1, i2) {
[this.heap[i1], this.heap[i2]] = [this.heap[i2], this.heap[i1]];
}
// 上浮
shiftUp(index) {
// 找 父节点
if (index === 0) return;
let fatherIndex = this.getFatherIndex(index);
if (this.heap[fatherIndex] > this.heap[index]) {
this.swap(fatherIndex, index);
// 继续
this.shiftUp(fatherIndex);
}
}
// 下沉
shiftDown(index) {
let leftIndex = this.getLeftIndex(index);
let rightIndex = this.getRightIndex(index);
// 左边
if (this.heap[leftIndex] < this.heap[index]) {
this.swap(leftIndex, index);
this.shiftDown(leftIndex);
}
// 右边
if (this.heap[rightIndex] < this.heap[index]) {
this.swap(rightIndex, index);
this.shiftDown(rightIndex);
}
}
// 删除
pop() {
// let top = this.heap[0];
this.heap[0] = this.heap.pop();
// 从头交换
this.shiftDown(0);
// return top;
}
// 插入
insert(value) {
this.heap.push(value);
// 重新整理 从尾部
this.shiftUp(this.heap.length-1);
}
// top
peek() {
return this.heap[0];
}
// size
size() {
return this.heap.length;
}
}
var KthLargest = function(k, nums) {
this.k = k;
this.minHeap = new MinHeap();
nums.forEach((item) => {
})
}
KthLargest.prototype.add = function(val) {
this.minHeap.insert(val);
// 多了拿走
if (this.minHeap.size() > this.k) {
this.minHeap.pop();
}
// 此时拿到的就是 第K大的元素
return this.minHeap.peek();
};
215. 数组中的第K个最大元素
代码实现:
class MinHeap {
// construcrtor() {
// this.heap = [];
// } 拼错了
constructor() {
this.heap = [];
}
// 寻找父节点 index
getFather(index) {
let fatherIndex = (index - 1) >> 1;
return fatherIndex
}
// 寻找 左子树 index
getLeftIndex(index) {
let leftIndex = index * 2 + 1;
return leftIndex;
}
// 寻找 右子树 节点index
getRightIndex(index) {
let rightIndex = 2 * index + 2;
return rightIndex;
}
swap(i1, i2) {
[this.heap[i1], this.heap[i2]] = [this.heap[i2], this.heap[i1]]; // 这里写错了 l1
}
// 上浮
shiftUp(index) {
// 临界点的判断
if (index === 0) return;
// 先拿到 父节点
let fatherIndex = this.getFather(index);
// 移动
if (this.heap[fatherIndex] > this.heap[index]) {
this.swap(fatherIndex, index);
// 继续移动
this.shiftUp(fatherIndex);
}
}
// 下沉
shiftDown(index) {
// 找左右
let leftIndex = this.getLeftIndex(index);
let rightIndex = this.getRightIndex(index);
// 分别比较左右节点 error 在于这数组不是链表
if (this.heap[leftIndex] < this.heap[index]) {
this.swap(leftIndex, index);
// 还没完
this.shiftDown(leftIndex);
}
// 右
if (this.heap[rightIndex] < this.heap[index]) {
this.swap(rightIndex, index);
// 还没完
this.shiftDown(rightIndex);
}
}
// 插入节点
insert(value) {
console.log(value)
this.heap.push(value)
// 上浮 填索引
this.shiftUp(this.heap.length - 1);
}
// 少了个删除 堆顶元素 没记住
pop() {
// if (this.size() === 1) return this.heap.shift();
// let top = this.heap[0];
// 此时 要拿最后一个 扔到第一个
this.heap[0] = this.heap.pop();
// 调整从堆顶开始重新排序 index = 0;
this.shiftDown(0)
// return top;
}
peek() {
// 没有 return
return this.heap[0];
}
size() {
return this.heap.length;
}
}
var findKthLargest = function(nums, k) {
// 把所有元素放到堆里面
let heap = new MinHeap();
for (let i of nums) {
if (heap.size() > k) {
heap.pop()
}
}
return heap.peek()
}
23. 合并k个升序链表
实现思路:
代码实现:
class MinHeap {
constructor() {
this.heap = [];
}
swap(i1, i2) {
[this.heap[i1], this.heap[i2]] = [this.heap[i2], this.heap[i1]];
}
//父节点index
getFatherIndex(index) {
return (index - 1) >> 1
}
// 左子树index
getLeftIndex(index) {
return (index * 2) + 1;
}
// 右子树
getRightIndex(index) {
return (index * 2) + 2;
}
// 上浮
shiftUp(index) {
// 首先会判断一下 边界条件
if (index === 0) return;
// 拿到父节点
let fatherIndex = this.getFatherIndex(index);
if (this.heap[fatherIndex] && this.heap[fatherIndex].val > this.heap[index].val) {
this.swap(fatherIndex, index); // 可以画图解决
// 继续
this.shiftUp(fatherIndex);
}
}
// 下沉
shiftDown(index) {
let leftIndex = this.getLeftIndex(index);
let rightIndex = this.getRightIndex(index);
// 左
if (this.heap[leftIndex] && this.heap[leftIndex].val < this.heap[index].val) {
this.swap(leftIndex, index);
// 继续
this.shiftDown(leftIndex)
}
// 右
if (this.heap[rightIndex] && this.heap[rightIndex].val < this.heap[index].val) {
this.swap(rightIndex, index);
// 继续
this.shiftDown(rightIndex)
}
}
// 插入
insert(value) {
this.heap.push(value);
// this.shiftUp(this.heap[this.heap.length-1]) 填索引
this.shiftUp(this.heap.length-1);
}
// 删除
pop() {
// 特殊情况处理 直接优化了
if (this.size() === 1) return this.heap.shift();
let top = this.heap[0];
// 这里是 需要重新调整 忘记了
this.heap[0] = this.heap.pop()
this.shiftDown(0);
return top;
}
peek() {
return this.heap[0]
}
size() {
return this.heap.length;
}
}
var mergeKLists = function(lists) {
// 对于链表还是一样的说法
const heap = new MinHeap();
const dummy = new ListNode(0);
let p = dummy;
// 插入k个升序链表的头部节点
lists.forEach(item => {
heap.insert(item);
})
//不断的比较最小堆中k个节点的大小
while(heap.size()) {
let temp = heap.pop(); // 取头 向下调整
p.next = temp; // 更新next ---> 节点
p = p.next; // p 指向下一个节点
// 如果 temp 的 下个节点存在 继续插入重新调整
// 向上调整
if (temp.next) heap.insert(temp.next)
}
// 返回头节点
return dummy.next;
}