JavaScript中二叉树(二叉堆)的介绍(代码示例)
二叉树
二叉树(Binary Tree)是一种树形构造,它的特点是每个节点最多只要两个分支节点,一棵二叉树平常由根节点,分支节点,叶子节点组成。而每个分支节点也常常被称作为一棵子树。
根节点:二叉树最顶层的节点
分支节点:除了根节点之外且具有叶子节点
叶子节点:除了本身,没有其他子节点
常用术语
在二叉树中,我们常常还会用父节点和子节点来描写,比方图中2为6和3的父节点,反之6和3是2子节点
二叉树的三个性质
在二叉树的第i层上,至多有2^i-1个节点
i=1时,只要一个根节点,2^(i-1) = 2^0 = 1
深度为k的二叉树至多有2^k-1个节点
i=2时,2^k-1 = 2^2 - 1 = 3个节点
对任何一棵二叉树T,假如总结点数为n0,度为2(子树数目为2)的节点数为n2,则n0=n2+1
树和二叉树的三个主要差异
树的节点个数至少为1,而二叉树的节点个数可认为0
树中节点的最大度数(节点数目)没有限制,而二叉树的节点的最大度数为2
树的节点没有摆布之分,而二叉树的节点有摆布之分
二叉树分类
二叉树分为完全二叉树(complete binary tree)和满二叉树(full binary tree)
满二叉树:一棵深度为k且有2^k - 1个节点的二叉树称为满二叉树
完全二叉树:完全二叉树是指最后一层左边是满的,右侧大概满也大概不满,然后其余层都是满的二叉树称为完全二叉树(满二叉树也是一种完全二叉树)
二叉树的数组表示
用一个数组来表示二叉树的构造,将一组数组从根节点开端从上到下,从左到右顺次填入到一棵完全二叉树中,如下图所示
通过上图我们可以剖析得到数组表示的完全二叉树具有以下几个性质:
left = index * 2 + 1,例如:根节点的下标为0,则左节点的值为下标array[0*2+1]=1
right = index * 2 + 2,例如:根节点的下标为0,则右节点的值为下标array[0*2+2]=2
序数 >= floor(N/2)都是叶子节点,例如:floor(9/2) = 4,则从下标4开端的值都为叶子节点
二叉堆
二叉堆由一棵完全二叉树来表示其构造,用一个数组来表示,但一个二叉堆需要知足如下性质:
二叉堆的父节点的键值总是大于或等于(小于或等于)任何一个子节点的键值
当父节点的键值大于或等于(小于或等于)它的每一个子节点的键值时,称为最大堆(最小堆)
从上图可以看出:
左图:父节点总是大于或等于其子节点,所以知足了二叉堆的性质,
右图:分支节点7作为2和12的父节点并没有知足其性质(大于或等于子节点)。
二叉堆的主要操纵
insert:插入节点
delete:删除节点
max-hepify:调整分支节点堆性质
rebuildHeap:从新构建整个二叉堆
sort:排序
初始化一个二叉堆
从上面简便的介绍,我们可以知道,一个二叉堆的初始化非常的简便,它就是一个数组
初始化一个数组构造
留存数组长度
class Heap{ constructor(arr){ this.data = [...arr]; this.size = this.data.length; } }
max-heapify最大堆操纵
max-heapify是把每一个不知足最大堆性质的分支节点停止调整的一个操纵。
如上图:
调整分支节点2(分支节点2不知足最大堆的性质)
默许该分支节点为最大值
将2与摆布分支比力,从2,12,5中寻出最大值,然后和2交流位置
按照上面所将的二叉堆性质,离别得到分支节点2的左节点和右节点
比力三个节点,得到最大值的下标max
假如该节点本身就是最大值,则休止操纵
将max节点与父节点停止交流
反复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)序号的都是我们需要调整的节点。
例如途中所示数组为[15,2,3,12,5,2,8,4,7] => Math.floor(9/2)=4 => index小于4的离别是15,2,3,12(需要调整的节点),而5,2,8,4,7为叶子节点。
将寻到的节点都停止maxHeapify操纵
rebuildHeap(){ // 叶子节点 const L = Math.floor(this.size / 2); for(let i = L - 1; i>=0; i--){ this,maxHeapify(i); } }
最大堆排序
最大堆的排序,如上图所示:
交流首尾位置
将最后个元素从堆中拿出,相当于堆的size-1
然后在堆根节点停止一次max-heapify操纵
反复以上三个步骤,知道size=0 (这个边界前提我们在max-heapify函数里已经做了)
sort() { for(let i = this.size - 1; i > 0; i--){ swap(this.data, 0, i); this.size--; this.maxHeapify(0); } }
插入和删除
这个的插入和删除就相对照较简便了,就是对一个数组停止插入和删除的操纵
往末尾插入
堆长度+1
推断插入后可否还是一个最大堆
不是则停止重构堆
insert(key) { this.data[this.size] = key; this.size++ if (this.isHeap()) { return; } this.rebuildHeap(); }
删除数组中的某个元素
堆长度-1
推断可否是一个堆
不是则重构堆
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这个操纵,乃至堆的三个性质。
以上就是JavaScript中二叉树(二叉堆)的介绍(代码示例)的具体内容,更多请关注百分百源码网其它相关文章!