假设现在我们已经把数据存到数组中:[1, 3, 4, 6, 8, 10, 2, 5]
,其存储可表示如下:
对于任意节点,我们怎么找到它的父节点和左右子节点?
1 | 索引为0的节点,没有父节点,左子节点索引为1,右子节点索引为2; |
将数组表示二叉树的形式,应该长这样(最左边数字表示树的层序号):
表示成二叉树结构之后,我们该怎么索引节点? 一般会用到以下两个参数:
level
,节点所在的层,从1开始order
,节点在当前层中的序号(从左至右),从1开始
例如上图,节点3可以通过level = 2, order = 1
来定位,再如节点8,可以通过level = 3, order = 2
来定位。
观察一下,我们可以发现以下规律(从根节点开始顺序左往右数):
1 | 第一层第一个节点序号:1 |
有了以上规律,我们现在就可以来映射一下level
、order
与数组下标之间的关系:
1 | // storage mapping function |
为啥-2?因为数组的索引是从0开始的,而我定义的level、order都是从1开始的(其实你也可以让它们从0开始)。
通过以上函数获取索引之后,就可以直接从数组中取出节点。
基本操作
先定义一个构造器,表示二叉树类:
1 | function SqBinaryTree(data) { |
data
参数表示用户输入的数据,二叉树构造器要做的就是把数据存储到nodes
中,然后我们就可以用树的形式来访问数据。
现在我们就可以通过以下方式来获得一颗顺序存储二叉树:
1 | var sbt = new SqBinaryTree([1, 3, 4, 6, 8, 10, 2, 5]); |
获取深度
一棵树的深度就是它的层数,怎么计算呢?
顺序存储可能不是紧凑的,比如:[1, undefined, 3, 2, 10, 12]
,其中的undefined表示该位置没有节点。
因此要计算深度得先从后往前找到最后一个非undefined的节点,该节点所在的索引+1就是总的节点数,然后就可以用一个循环来计算深度:
1 | SqBinaryTree.prototype.getDepth = function() { |
例如:
1 | sbt.getDepth(); // 4 |
获取节点
获取指定位置的节点:
1 | SqBinaryTree.prototype.getNode = function(level, order) { |
插入节点
在指定位置上插入节点时,如果待插入节点的位置上没有对应的父节点,则无法插入,如下图所示:
红色圆框表示待插入节点的位置,虚线框表示该位置上没有节点,此时如果在红色圆框位置插入节点,因为红色圆框的父节点虚线框的位置上不存在节点,因此插入失败:
1 | SqBinaryTree.prototype.setNode = function(level, order, value) { |
删除节点
本文实现的二叉树是一个普通的二叉树,没有特殊二叉排序树那种约束,因此删除节点时就不需要重建树了,直接把待删除节点的整颗子树都删掉即可:
1 | SqBinaryTree.prototype.deleteNode = function(level, order) { |
源代码已经放在Github上了,有兴趣的可以围观一下,传送门。