【数据结构】【学习笔记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;
	}

}

思维导图

image

扩展

在内存下堆的意思就不一样了,堆是环境提供给程序用来动态分配内存的一大片自由存储区域。

局部变量在栈,而new的在堆,大概是这样子。

下图为Gemini生成的对比内容。

image

posted @ 2026-07-17 15:44  SEHOD  阅读(0)  评论(0)    收藏  举报