二叉树(Binary Tree)是一种树形结构,它的特色是每一个节点最多只有两个分支节点,一棵二叉树一般由根节点,分支节点,叶子节点组成。而每一个分支节点也经常被称做为一棵子树。前端
经常使用术语
在二叉树中,咱们经常还会用父节点和子节点来描述,好比图中2为6和3的父节点,反之6和3是2子节点api
在二叉树的第i层上,至多有2^i-1个节点数组
深度为k的二叉树至多有2^k-1个节点函数
二叉树分为彻底二叉树(complete binary tree)和满二叉树(full binary tree)ui
用一个数组来表示二叉树的结构,将一组数组从根节点开始从上到下,从左到右依次填入到一棵彻底二叉树中,以下图所示this
经过上图咱们能够分析获得数组表示的彻底二叉树拥有如下几个性质:spa
二叉堆由一棵彻底二叉树来表示其结构,用一个数组来表示,但一个二叉堆须要知足以下性质:code
从上图能够看出:视频
从上面简单的介绍,咱们能够知道,一个二叉堆的初始化很是的简单,它就是一个数组blog
class Heap{ constructor(arr){ this.data = [...arr]; this.size = this.data.length; } }
max-heapify是把每个不知足最大堆性质的分支节点进行调整的一个操做。
如上图:
调整分支节点2(分支节点2不知足最大堆的性质)
将2与左右分支比较,从2,12,5中找出最大值,而后和2交换位置
重复step2的操做,从2,4,7中找出最大值与2作交换
maxHeapify(i) { let max = i; if(i >= this.size){ return; } // 当前序号的左节点 const l = i * 2 + 1; // 当前须要的右节点 const r = i * 2 + 2; // 求当前节点与其左右节点三者中的最大值 if(l < this.size && this.data[l] > this.data[max]){ max = l; } if(r < this.size && this.data[r] > this.data[max]){ max = r; } // 最终max节点是其自己,则已经知足最大堆性质,中止操做 if(max === i) { return; } // 父节点与最大值节点作交换 const t = this.data[i]; this.data[i] = this.data[max]; this.data[max] = t; // 递归向下继续执行 return this.maxHeapify(max); }
咱们能够看到,刚初始化的堆由数组表示,这个时候它可能并不知足一个最大堆或最小堆的性质,这个时候咱们可能须要去将整个堆构建成咱们想要的。
上面咱们作了max-heapify操做,而max-heapify只是将某一个分支节点进行调整,而要将整个堆构建成最大堆,则须要将全部的分支节点都进行一次max-heapify操做,以下图,咱们须要依次对12,3,2,15这4个分支节点进行max-hepify操做
具体步骤:
找到全部分支节点:上面堆的性质提到过叶子节点的序号>=Math.floor(n/2),所以小于Math.floor(n/2)序号的都是咱们须要调整的节点。
rebuildHeap(){ // 叶子节点 const L = Math.floor(this.size / 2); for(let i = L - 1; i>=0; i--){ this,maxHeapify(i); } }
最大堆的排序,如上图所示:
sort() { for(let i = this.size - 1; i > 0; i--){ swap(this.data, 0, i); this.size--; this.maxHeapify(0); } }
这个的插入和删除就相对比较简单了,就是对一个数组进行插入和删除的操做
insert(key) { this.data[this.size] = key; this.size++ if (this.isHeap()) { return; } this.rebuildHeap(); }
delete(index) { if (index >= this.size) { return; } this.data.splice(index, 1); this.size--; if (this.isHeap()) { return; } this.rebuildHeap(); }
/** * 最大堆 */ function left(i) { return i * 2 + 1; } function right(i) { return i * 2 + 2; } function swap(A, i, j) { const t = A[i]; A[i] = A[j]; A[j] = t; } class Heap { constructor(arr) { this.data = [...arr]; this.size = this.data.length; } /** * 重构堆 */ rebuildHeap() { const L = Math.floor(this.size / 2); for (let i = L - 1; i >= 0; i--) { this.maxHeapify(i); } } isHeap() { const L = Math.floor(this.size / 2); for (let i = L - 1; i >= 0; i++) { const l = this.data[left(i)] || Number.MIN_SAFE_INTEGER; const r = this.data[right(i)] || Number.MIN_SAFE_INTEGER; const max = Math.max(this.data[i], l, r); if (max !== this.data[i]) { return false; } return true; } } sort() { for (let i = this.size - 1; i > 0; i--) { swap(this.data, 0, i); this.size--; this.maxHeapify(0); } } insert(key) { this.data[this.size++] = key; if (this.isHeap()) { return; } this.rebuildHeap(); } delete(index) { if (index >= this.size) { return; } this.data.splice(index, 1); this.size--; if (this.isHeap()) { return; } this.rebuildHeap(); } /** * 堆的其余地方都知足性质 * 惟独跟节点,重构堆性质 * @param {*} i */ maxHeapify(i) { let max = i; if (i >= this.size) { return; } // 求左右节点中较大的序号 const l = left(i); const r = right(i); if (l < this.size && this.data[l] > this.data[max]) { max = l; } if (r < this.size && this.data[r] > this.data[max]) { max = r; } // 若是当前节点最大,已是最大堆 if (max === i) { return; } swap(this.data, i, max); // 递归向下继续执行 return this.maxHeapify(max); } } module.exports = Heap;
堆讲到这里就结束了,堆在二叉树里相对会比较简单,经常被用来作排序和优先级队列等。堆中比较核心的仍是max-heapify这个操做,以及堆的三个性质。
下一篇应该会介绍二叉搜索树。欢迎你们指出文章的错误,若是有什么写做建议也能够提出。我会持续的去写关于前端的一些技术文章,若是你们喜欢的话能够关注一和点个赞,你的赞是我写做的动力。
顺便再提一下,我在等第一个粉丝哈哈
如下我的公众号,欢迎你们关注,用户量达到必定的量,我会推出一些前端教学视频