这是我参与8月更文挑战的第13天,活动详情查看:8月更文挑战
上一篇讲了堆和优先队列的基本概念,有兴趣的小伙伴请冲 堆与优先队列概念。 这篇我们用js来实现一个大顶堆。
一、基础代码
我们先来写下基础代码
- 先创建一个类Heap表示大顶堆;
- 初始化基本数据:用数组data来表示堆,用变量length表示堆的元素个数;
- 设置top方法:返回堆的最顶上元素,也即最大的元素;
- 设置size方法:返回堆的元素个数。
class Heap {
constructor() {
this.data = [];
this.length = 0; // 堆元素个数
}
top() {
return this.data[0];
}
size() {
return this.length;
}
}
二、插入元素
在堆中插入元素,是先将元素插入堆的最底层的最右边,然后再与其父元素进行对比,若大于父元素,则与父元素调换位置,调换位置后再与其现在父元素对比...直到不大于其父元素才确认位置。可以简单概括为插入元素时上浮。
- 那如何知道堆的元素个数呢?
设定一个变量length,每次插入时都给length+1。
- 那如何确定父元素的索引呢?
根据堆的性质,假如当前元素的索引为ind,则其父节点的索引为Math.floor((ind - 1) / 2)。
如下图所示:
下面我们直接上代码啦~~
push(x) {
// 将x放入堆的最下层的最右边,并将长度+1
this.data[this.length++] = x;
let ind = this.length - 1; // ind设为最后一个元素的索引
// 判断当前元素是否大于父元素,若是则交换位置,交换位置后再继续对比...
while (ind && this.data[ind] > this.data[Math.floor((ind - 1) / 2)]) {
this.swap(ind, Math.floor((ind - 1) / 2));
ind = Math.floor((ind - 1) / 2);
}
}
swap(a, b) {
let temp = this.data[a];
this.data[a] = this.data[b];
this.data[b] = temp;
}
测试代码:
- let h = new Heap();
- h.push(4); // [4]
- h.push(1); // [4,1]
- h.push(2); // [4, 1, 2]
- h.push(3); // [4, 3, 2, 1]
- h.push(5); // [5, 4, 2, 1, 3]
经过测试,没啥毛病,奈斯~~
三、删除元素
删除元素的步骤:
- 先将大顶堆中最顶部元素(即最大元素)与最底层最右边的元素互换位置,然后删除最大元素,堆元素长度-1。
- 判断当前元素是否有子节点,若有子节点执行下沉操作。 下沉操作的实质是对比当前节点与左右 节点哪个大,将当前节点与最大的节点互换位置,换完后再进行对比...
根据堆的性质,若当前元素索引为ind,则左节点的索引为 ind * 2 + 1,右节点的索引为 ind * 2 + 2。
知道了基本操作步骤后我们来看看代码~~~
pop() {
if (this.size() == 0) return; // 若堆为空则不操作
this.swap(0, this.length - 1); // 调换堆中最顶部元素 和 最尾部元素的位置
this.data.pop();
this.length--; // 将堆元素个数-1
// 如果有孩子执行下沉操作
let ind = 0;
// 对比当前节点与左右 节点哪个大,将当前节点与最大的节点互换位置
// 当ind有子节点时才能操作,左节点的索引ind*2+1小于等于堆长度-1时证明左节点存在
while (ind * 2 + 1 <= this.length - 1) {
// 当前节点先跟左子树对比,则将temp设为左节点的索引
let temp = ind;
if (this.data[temp] < this.data[ind * 2 + 1]) {
temp = ind * 2 + 1;
}
// 如果右节点存在,则再和右节点对比
if (
this.data[ind * 2 + 2] &&
this.data[temp] < this.data[ind * 2 + 2]
) {
temp = ind * 2 + 2;
}
// 若temp等于ind,则不需要交换位置,否则,交换temp和ind的元素位置,ind重新赋值
if (temp == ind) break;
this.swap(ind, temp);
ind = temp;
}
}
测试一下:
- let h = new Heap();
- h.push(4); // [4]
- h.push(1); // [4,1]
- h.push(2); // [4, 1, 2]
- h.push(3); // [4, 3, 2, 1]
- h.push(5); // [5, 4, 2, 1, 3]
- h.pop(); // [4, 3, 2, 1]
- h.pop(); // [3, 1, 2]
好的,完全没毛病,结束战斗,明天见~~~