一. 树的概念
定义:树是一种非线性的数据结构。由n(n>=0)个节点组成.
1. 节点
它包含数据项,和指向其它节点的指针,上图中的树有7个节点。
2.节点的度
有几个分支,度就是几
3. 叶节点
度为0的节点被称为叶节点, 上图中C、D、F为叶节点
4.分支节点
除了叶节点之外的节点就是分支节点
5. 节点层次
节点所在的层级,如根节点A在第一层
6.树的深度
树中距离根节点最远的节点所处的层次就是树的深度,如上图为3
7. 树的度
树中节点的度的最大值。 上图中根节点的度最大,为3
8. 森林
森林是m(m>=0)颗数的集
二.二叉树
定义:二叉树是一种特殊情况,每个节点最多有两个子女,分别为左子女和右子女
二叉树的性质:
a. 二叉树的第i(i>=1)层,最多存在2的i-1次方个节点。
b.深度为k(k>=0)的二叉树,最少有K个节点,最多有2的k-1次方个节点。
c. 对于一个非空二叉树,叶节点的数量等于度为2的节点数量加1。
特殊二叉树:
满二叉树: 深度为k的满二叉树,是有2的k-1次方个节点的二叉树,每一层都达到了可以容纳的最大数量的节点
完全二叉树: 深度为k的完全二叉树,从第一层到第k-1层都是满的,第k层,或是满的,或是从右向左连续缺若干个节点
三.code定义二叉树
// 定义二叉树节点
class BinTreeNode {
constructor(data) {
this.data = data
this.parentNode = null //父节点
this.leftChild = null //左节点
this.rightChild = null //右节点
}
}
遍历广义表字符串,来建立一颗二叉树。
广义表A(B(D,E(G,)),C(,F))#: 广义表的表名放在表前,表示树的根节点,括号中的是根的左右子树,整个广义表的最后加上特殊符号#表示输入结束
实现思路:
*遇到左括号的时候,说明前面有一个节点,这个括号里的两个节点都是它的子节点,但是子节点后面还会有子节点,因此,我们需要一个先进后出的数据结构把前面的节点保存下来,这样栈顶就是当前要处理的两个节点的父节点;
*逗号分隔了左右子树,因此需要一个变量来标识遇到的是左子树还是右子树,假设这个变量为k,遇到左括号的时候,k=1,表示开始识别左子树;
*遇到逗号,k=2表示开始识别右子树;
*遇到右括号,说明一棵子树结束了,那么栈顶的元素就是这颗子树的根节点,执行pop方法出栈
class BinTree {
constructor () {
this.root = null //根节点
}
/**
* 初始化二叉树
*/
initTree (str) {
if (!str) return
var flag = 0
let newNode = null
let stack = new Stack()
for (let i = 0; i < str.length; i++) {
// 标记左右子树
let item = str[i]
// # 结束标志
if (item === '#') {
break
}
if (item === '(') {
flag = 1
stack.push(newNode)
} else if (item === ',') {
flag = 2
} else if (item === ')') {
stack.pop()
} else {
// 创建新节点
newNode = new BinTreeNode(item)
if (!this.root) {
this.root = newNode
} else {
// k为1添加左节点,2添加右节点
if (flag === 1) {
let topNode = stack.top()
topNode.leftChild = newNode
newNode.parentNode = topNode
} else if (flag === 2) {
let topNode = stack.top()
topNode.rightChild = newNode
newNode.parentNode = topNode
}
}
}
}
}
/**
* 树的根节点
*/
getRoot () {
return this.root
}
getTreeCount (node) {
if (!node) return 0
// 返回的是左孩子节点和有孩子节点再加上当前节点个数
let leftSize = this.getTreeCount(node.leftChild)
let rightSize = this.getTreeCount(node.rightChild)
return leftSize + rightSize + 1
}
/**
* 树的节点个数
*/
size () {
return this.getTreeCount(this.root)
}
getTreeHeight (node) {
if (!node) return 0
// 先计算左右节点的深度
let leftHeight = this.getTreeHeight(node.leftChild)
let rightHeight = this.getTreeHeight(node.rightChild)
// 返回较大者
if (leftHeight > rightHeight) {
return leftHeight + 1
} else {
return rightHeight + 1
}
}
/**
* 树的高度(深度)
*/
height () {
this.getTreeHeight(this.root)
}
findTreeNode (node, data) {
if (!node) return null
if (node.data === data) return node
// 先找左节点
let leftNode = this.findTreeNode(node.leftChild)
if (leftNode) {
return leftNode
}
// 没找到,从右节点找
return this.findTreeNode(node.rightHeight)
}
/**
* 查找节点
*/
findNode (data) {
this.findTreeNode(this.root, data)
}
/**
* 前序遍历(当前节点->当前节点的左节点->当前节点的右节点)
*/
preOrder (node) {
if (!node) return
console.log(node.data)
this.preOrder(node.leftChild)
this.preOrder(node.rightChild)
}
/**
* 中序遍历(当前节点的左节点->当前节点->当前节点的右节点)
*/
inOrder (node) {
if (!node) return
this.inOrder(node.leftChild)
console.log(node.data)
this.inOrder(node.rightChild)
}
/**
* 后序遍历(当前节点的左节点->当前节点的右节点->当前节点)
*/
postOrder (node) {
if (!node) return
this.postOrder(node.leftChild)
this.postOrder(node.rightChild)
console.log(node.data)
}
}
定义栈:
class Stack {
constructor() {
this.items = []
}
push (data) {
this.items.push(data)
}
pop () {
let popItem = this.items.pop()
return popItem
}
top () {
return this.items[this.items.length - 1]
}
size () {
return this.items.length
}
clear () {
this.items = []
}
isEmpty () {
return this.items.length === 0
}
}
测试代码:
let binTree = new BinTree()
binTree.initTree("A(B(D,E(G,)),C(,F))#")
binTree.preOrder(binTree.root) // A B D E G C F
console.log(binTree.size())
扩展:
1. 树的镜像:对于一棵树,如果每个节点的左右子树互换位置,那么就变成了这颗树的镜像。
function mirror (node) {
if (!node) return
let temp = node.leftChild
node.leftChild = node.rightChild
node.rightChild = temp
mirror(node.leftChild)
mirror(node.rightChild)
}
mirror(binTree.root)
binTree.inOrder(binTree.root) // F C A E G B D
2. 前序遍历的非递归实现
function preOrder(node){
var stack = new Stack()
var curr_node = node
while (curr_node) {
console.log(curr_node.data)
if (curr_node.rightChild) {
stack.push(curr_node.rightChild)
}
if (curr_node.leftChild) {
curr_node = curr_node.leftChild
} else {
//没有左子树
curr_node = stack.pop()
}
}
}
preOrder(binTree.root)