今天简单聊聊一个称为堆的数据结构
堆(Heap)是计算机科学中一类特殊的数据结构的统称。堆通常是一个可以被看做一棵完全二叉树的数组对象。--百度百科
堆结构的使用场景
-
构建优先队列(堆顶总是最大(最小)元素)
-
堆排序
-
寻找最大值/最小值
分类
最大堆(大顶堆)
每个父节点的值大于其子节点
最小堆(小顶堆)
每个父节点的值小于其子节点
堆和查找二叉树的区别
节点顺序
查找二叉树的右节点的值比父节点大,左节点的值比父节点小。而堆只要求子节点的值都比父节点小(大)。
内存占用
二叉树的数据结构包含了父节点对子节点的引用关系(指针),内存占用较高。而堆使用数组来保存数据,不包含引用关系,内存占用小。
搜索
因为二叉树的父子节点的相对大小有规律(右节点>父节点>左节点),并且保存了父子节点的引用关系,所以搜索效率高。而堆只限定了父节点和子节点的相对大小,搜索效率比二叉树低。
堆的实现
根据定义我们是使用数组来保存堆的,但是堆的数据结构又像二叉树一般有父节点,左右子节点,那么它们在数组的位置有何规律呢?
parent(i) = Math.floor((i - 1)/2);
left(i) = 2i + 1;
right(i) = 2i + 2;
下面是根据堆元素的位置关系实现的堆(最大堆),主要包含添加元素和删除堆顶方法
class Heap {
constructor() {
// 堆数据结构为数组
this.data = [];
}
// 添加元素
insert(data) {
// 添加到末尾
this.data.push(data);
let i = this.data.length - 1;
// 调整元素位置
this.shiftUp(i);
}
// 上滤(自下而上调整)
shiftUp(i) {
while(1) {
let parent = Math.floor((i - 1) / 2);
let cur = this.data[i];
if (parent < 0 || this.data[parent] >= cur) {
break;
}
this.data[i] = this.data[parent];
this.data[parent] = cur;
i = parent;
}
}
// 删除堆顶元素
removeRoot() {
const root = this.data[0];
if (this.data.length < 1) {
return null;
}
if (this.data.length === 1) {
this.data = [];
return root;
}
// 末尾元素提前,同时删除堆顶
this.data[0] = this.data[this.data.length - 1];
this.data.pop();
// 调整元素位置
this.shiftDown(0);
return root;
}
// 下滤(自上而下调整)
shiftDown(i) {
const len = this.data.length;
while(1) {
const leftIdx = 2 * i + 1;
const rightIdx = 2 * i + 2;
const left = this.data[2 * i + 1];
const right = this.data[2 * i + 2];
const cur = this.data[i];
if (leftIdx >= len || (left < cur && right < cur)) {
break;
}
if (left > right) {
if (left > cur) {
this.data[i] = this.data[leftIdx];
this.data[leftIdx] = cur;
i = leftIdx;
}
} else if (right > left) {
if (right > cur) {
this.data[i] = this.data[rightIdx];
this.data[rightIdx] = cur;
i = rightIdx;
}
} else {
break;
}
}
}
}
因为堆的作用主要就在于堆顶的运用,所以我们只实现堆顶的移除方法,这已经满足大部分我们使用到堆顶的场景。
参考
欢迎到前端学习打卡群一起学习~516913974