树和森林
树和森林的定义
- 树:和二叉树的定义类似,不同在于树中的节点可以有
0个或者多个后继节点 - 森林:多棵不相交的树的集合
树的存储
- 双亲表示法
// 节点
class Node {
constructor (data, parentIndex) {
this.data = data || null
this.parentIndex = parentIndex || -1
}
}
// 使用连续存储空间存储树中的各个节点
class Tree {
constructor (length) {
this.data = new Array(length)
}
addNode (node) {
this.data.push(node)
}
}
const tree = new Tree(6)
tree.addNode(new Node('A', -1))
tree.addNode(new Node('B', 0))
tree.addNode(new Node('C', 0))
tree.addNode(new Node('D', 1))
tree.addNode(new Node('E', 1))
tree.addNode(new Node('F', 1))
双亲表示法查找双亲节点只要常数时间,但是如果查找节点的孩子节点则必须遍历整个树的节点,效率低下
- 孩子表示法
class ChainNode {
constructor (childIndex, next) {
this.childIndex = childIndex
this.next = next || null
}
}
class Node {
constructor (data, firstChildIndex) {
this.data = data
this.firstChildIndex = firstChildIndex || null
}
}
class Tree {
constructor (length) {
this.data = new Array(length)
}
addNode (node) {
this.add.push(node)
}
}
const tree = new Tree(6)
tree.addNode(new Node('A'))
tree.addNode(new Node('B'))
tree.addNode(new Node('C'))
tree.addNode(new Node('D'))
tree.addNode(new Node('E'))
tree.addNode(new Node('F'))
const a = new ChainNode(1, b)
const b = new ChainNode(2)
tree.data[0].firstChildIndex = a
const d = new ChainNode(3, e)
const e = new ChainNode(4, f)
const f = new ChainNode(5)
tree.data[1].firstChildIndex = d
孩子表示法查找节点的孩子节点是O(1)的,但查找节点的双亲节点却效率较低
- 双亲孩子表示法
顾名思义就是结合上面两种的优点完成树的存储,能够快速的查找出节点的孩子节点和双亲节点
class ChainNode {
constructor (childIndex, next) {
this.childIndex = childIndex
this.next = next || null
}
}
class Node {
constructor (data, parentIndex, firstChildIndex) {
this.data = data
this.parentIndex = parentIndex || -1 // 指向双亲节点的下标
this.firstChildIndex = firstChildIndex || null // 指第一孩子的下标
}
}
class Tree {
constructor (length) {
this.data = new Array(length)
}
addNode (node) {
this.add.push(node)
}
}
const tree = new Tree(6)
tree.addNode(new Node('A', -1))
tree.addNode(new Node('B', 0))
tree.addNode(new Node('C', 0))
tree.addNode(new Node('D', 1))
tree.addNode(new Node('E', 1))
tree.addNode(new Node('F', 1))
const a = new ChainNode(1, b)
const b = new ChainNode(2)
tree.data[0].firstChildIndex = a
const d = new ChainNode(3, e)
const e = new ChainNode(4, f)
const f = new ChainNode(5)
tree.data[1].firstChildIndex = d
该存储方法可以快速的定位到节点的孩子和双亲节点
- 孩子兄弟表示法
class Node {
construcrot (data, firstChild, brother) {
this.data = data
this.firstChild = firstChild || null
this.brother = brother || null
}
}
const a = new Node('A', b, null)
const b = new Node('B', d, c)
const c = new Node('C', null, null)
const d = new Node('D', null, e)
const e = new Node('E', null, f)
const f = new Node('F', null, null)
该表示法采用链式存储了树
森林的存储
- 双亲表示法
// 和树的存储一样,把森林的所有节点放入一个一维数组,只是多了几个根节点而已(指向双亲的指针为-1)
// 可以再使用一个一维数组保存各个树的根节点的索引下标
- 孩子表示法
// 类似与 森林的双亲表示法
- 孩子兄弟表示法
// 把森林中的第一棵树用 孩子兄弟表示法保存后,其余的树则可以当作 第一棵树的兄弟节点进行保存
树和森林与二叉树的转换
- 树和二叉树的转换
由下面的转换规则可知:
树转化成二叉树后,它的根节点一定没有右子树
根节点没有右子树的二叉树转成二叉树后,一定是一个树(如果有右子树,则会转化成森林)
-
森林和二叉树的转换
森林和二叉树的转化和 树与二叉树的转化是一摸一样的(也就是 加线,去线,旋转)
树与森林的遍历
-
树的遍历
先序遍历
后序遍历
层次遍历 -
森林的遍历
先序遍历
中序遍历
树和森林如果使用 二叉链表进行存储的话,遍历等操作可以使用二叉树的方法
(树和森林的链式存储和转化成二叉树后的结构一样,只是具体含义不同)