一开始我很迷惑,js中有堆这种东西吗,数组和堆怎么做转换?
满脸问号,一头雾水。查找了一大圈, 我终于悟了。
js中的堆最简单就是直接把数组当成堆, 通过索引去关联自己的子项:
比如索引[0]的元素,它的左右两个子项分别是索引[1]、 索引[2]的元素;
然后索引[1]的子项是索引[3]、索引[4]。
转换公式为: 索引i的子项等于 2i + 1 、 2i + 2
什么是堆?
堆的预备知识
-
堆是一个近似完全二叉树的结构,并同时满足堆积的性质:即子结点的键值或索引总是小于(或者大于)它的父节点;
-
完全二叉树: 二叉树除开最后一层,其他层结点数都达到最大,最后一层的所有结点都集中在左边(左边结点排列满的情况下,右边才能缺失结点);
完全二叉树图示如下:
新增元素一定是从左往右依次添加!!! -
其中堆又分为大顶堆和小顶堆。大顶堆意味着从上往下,父节点的值永远大于子节点。小顶堆则反过来。
大顶堆和小顶堆图示如下:
如何实现一个堆排序?
堆排序有两个核心方法:
初始化方法,一开始要对倒数第二层开始,每个节点确保是大于自己的子节点的,如果不大于就交换位置;一层层往上递进直到根节点为止。
插入元素的时候,我们直接插入到头部, 然后当它的值大于它的父元素时,不断与父元素交换位置冒泡下去,直到达到底层节点为止。如果节点已经有序了,那么我们可以把它跟最后的末尾节点交换位置,然后对新的头节点做同样的操作,重复N轮即可对整个数组排序。
堆排序的实现
// 堆排序
function heapSort(arr) {
buildMaxHeap(arr);
const n = arr.length - 1;
// 把当前的值放到最后面,然后把最后面的值作为头节点
// 然后调整堆的顺序即可
for (let i = n; i > 0; i--) {
swap(arr, 0, i);
heapify(arr, i - 1);
}
}
// 交换节点的值
function swap(arr, i, j) {
const temp = arr[i];
arr[i] = arr[j];
arr[j] = temp;
}
// 建立最大堆,初始化数据的时候操作
function buildMaxHeap(arr) {
const n = arr.length - 1;
// 最后一个数的父节点是(n - 1) / 2 | 0;
const cur = (n - 1) / 2 | 0;
// 从倒数第二层开始排,最后一层不用调整;
for (let i = cur; i >= 0; i--) {
heapify(arr, n, i);
}
}
// 调整堆顶元素所在的位置, 如果比下层元素小就往下交换位置
function heapify(arr, len, i = 0) {
let left = i * 2 + 1;
let right = i * 2 + 2;
let maxIndex = i;
// 左节点是最大值的话
if (left <= len && arr[left] > arr[maxIndex]) {
maxIndex = left;
}
// 右节点是最大值的话
if (right <= len && arr[right] > arr[maxIndex]) {
maxIndex = right;
}
// 如果当前节点不是最大值,那么当前节点的值要往下传递,让下面值大的冒泡上来
if (maxIndex !== i) {
swap(arr, i, maxIndex);
heapify(arr, len, maxIndex);
}
}
堆排序的使用场景 -- 优先队列
在我们常用的数据结构中,有队列和栈这两个概念,其中队列是先进先出,栈是先进后出,那么当我们想按照我们指定的顺序,不管先进后进,优先级高的先出的时候,那么就可以用优先队列来实现了。比如说我们做一个调度功能,有三条指令待下发,分别是[ 选择制冷模式 、 开启空调、 温度调为20℃ ];正常来讲肯定是要先开启空调才去做其他两个动作的,所以我们可以给每个指令身上加上一个属性:sortNum让他手动进行排序。常规操作我们得对所有数据做一个排序,然后每次取出优先级最高的那一条。但是实际上我们只需要保证每次能拿到优先级最高的元素,其他元素的排列顺序我们并不关心,这时候就可以使用优先队列来实现。因为我们知道,在堆排序中父节点的优先级总是大于子节点的优先级。
堆排序和优先队列的区别
堆排序是一种排序方式,相当于我们的Array.sort这种方法,可以拿来操作数组改变它的元素位置;
优先队列是一种数据结构,是基于堆排序实现的,相当于一个特殊的Array,只不过它指定了自己的排序方式必须是堆排序。
堆排序和优先队列实战
由于这部分的篇幅过长,我单独另起了一篇文章来记录[路飞]_一起刷leetcode 692. 前K个高频单词
看懂了的小伙伴可以点个关注、咱们下道题目见。如无意外以后文章都会以这种形式,有好的建议欢迎评论区留言。