【数据结构】【学习笔记001】堆
堆
定义
堆是一个特殊的完全二叉树,父节点永远大于/小于子节点。
所以堆顶一定是整个堆中的最大/小值。
完全二叉树:
- 高度都是 \(\log_2 N\)
- 二叉树中是没有空洞的,所以可以使用数组直接存放
- 获取索引的方式:(当前索引为 \(i\))
- 左孩子索引:\(2i+1\)
- 右孩子索引:\(2i+2\)
- 父节点索引:\(\lfloor \frac{i-1}{2} \rfloor\)
实现方式
核心思想
每次取了堆顶时,就将最后的数放在堆顶,然后慢慢将其下沉。
而插入数据时,放在最后,然后检查父节点,进行上浮。
一般来说应该不会取堆里面的某个值吧,只会取顶吧
1. 上浮
比较父节点,当父节点大于/小于本身时,交换位置,直到父节点小于/大于本身。
2. 下沉
比较自己的左右孩子,假如左孩子大于/小于自己,则和左孩子交换;假如右孩子大于/小于自己,则和右孩子交换;假如都不满足,则结束下沉。
实现用例
语言TypeSript
class MinPriorityQueue{
private queue:number[] = [];
// 插入数据
public enqueue(num:number):void{
this.queue.push(num);
this.flow(this.queue.length-1);
}
// 查顶
public peek():number|undefined{
return this.queue[0];
}
// 弹出顶
public dequeue():number|undefined{
if (this.queue.length === 0) return undefined;
const res = this.queue[0];
const bottom = this.queue.pop()!;
if(this.queue.length>0){
this.queue[0] = bottom;
this.down(0);
}
return res;
}
// 上浮
private flow(i:number): void{
while(i > 0){
const p = this.getParent(i);
if(this.queue[p] <= this.queue[i]){
break;
}
this.swap(i, p);
i = p;
}
}
// 下沉
private down(i:number):void{
const len = this.queue.length;
while(this.getLeftChild(i) < len){
let l = this.getLeftChild(i);
let r = l+1;
let best = i;
if(this.queue[l] < this.queue[i]){
best = l;
}
if(r < len && this.queue[r] < this.queue[best]){
best = r;
}
if(best === i){
break;
}
this.swap(i, best);
i = best;
}
}
// 获得索引
private getLeftChild(i:number):number{
return (i<<1)+1;
}
private getRightChild(i:number):number{
return (i<<1)+2;
}
private getParent(i:number):number{
return (i-1)>>1;
}
// 交换
private swap(a:number, b:number):void{
const temp = this.queue[a];
this.queue[a] = this.queue[b];
this.queue[b] = temp;
}
}
思维导图

扩展
在内存下堆的意思就不一样了,堆是环境提供给程序用来动态分配内存的一大片自由存储区域。
局部变量在栈,而new的在堆,大概是这样子。
下图为Gemini生成的对比内容。


浙公网安备 33010602011771号