强烈推荐这个up主的视频:https://www.bilibili.com/video/av47196993,声音好听,讲的又清楚。
什么是堆
首先,堆满足以下两个条件:
- 一定是一个完全二叉树的结构。
- 如果父节点 > 两个子节点 :称之为
大根堆
;
如果父节点 < 两个子节点 :称之为小跟堆
;
以大根堆为例:
可以知道,对于任意一个节点 i ,我们都可以找到其父节点和子节点的位置:
父节点: (i-1)/2 向上取整
左节点: i*2+1
右节点: i*2+2
首先考虑最小的堆,只有三个节点。
那么想把它变成一个堆,就比较根节点和左右节点,取出最大的值max作为根。
然后接着对max所在位置节点,继续比较它和它的左右节点。。。重复操作,直到叶子节点。
heapify函数能做到从根节点开始,一直到最下面的节点这一条路径满足堆的条件。
function heapify(tree,n,i){
if(i > n) return;
var c1 = i*2+1;
var c2 = i*2+2;
var max = i;
//确定max指针指向(根,左右节点中)最大的数
if( c1 < n && tree[max]<tree[c1]){
max = c1;
}
if ( c2 < n && tree[max]<tree[c2]) {
max = c2
}
//如果max指向的不是根,就把max指向的节点提到根上来
if(max !== i){
[tree[max],tree[i]] = [tree[i],tree[max]];
//对叶子节点继续进行建堆操作
heapify(tree,n,max);
}
// console.log(tree);
}
构建一个堆
构建一个堆,其实就是不停的进行heapify
过程。对于一个乱序的树,我们从最后一个非叶子节点开始进行heapify
。要找到最后一个非叶子节点,其实就是,最后一个最后一个叶子节点的父节点:
var parent = parseInt((lastNode-1)/2);
//从下至上构建堆,n是tree的长度
function buildHeap(tree,n){
var lastNode = n-1;
var parent = parseInt((lastNode-1)/2);
/*
这里一定是从最后一个非叶子节点开始,往上走,这样就保证该节点的子节点已经形成堆了,
只需考虑该节点和其父节点。如果从根节点开始向下走,比如这个例子中
会出现[5, 10, 3, 1, 2, 4 ],因为根节点已经heapify过了,不会再执行,导致10和5不会再交换
*/
for(var x = parent ; x >= 0 ; x--){
heapify(tree,n,x);
}
}
堆排序(Heap Sort)
堆排序:首先构建好一个堆,之后把root的值与最后一个叶子节点对调,切掉最后一个节点。之后重新heapify
,重复上述操作。
//堆排序
function heapSort(tree,n){
//初建一个堆
buildHeap(tree,6);
//循环,i作为数组最后节点的位置,每次减1,相当于切掉最大的值。
for (var i = n - 1; i >= 0; i--) {
//交换根节点与最后一个节点
[tree[i],tree[0]] = [tree[0],tree[i]];
//对剩余节点进行heapify,把剩余数中最大值提到tree[0]中
heapify(tree, i, 0);
}
}
heapSort(tree,6);
console.log(tree);