什么是树
基础概念
在数据结构中,树(Tree)是一种非线性的数据结构,它是由n(n>=0)个有限节点组成一个具有层次关系的集合。对于n>0时,这个集合满足以下条件:
- 有且仅有一个特定的称为根(root)的节点。
- 当n>1时,其余节点可分为m(m>0)个互不相交的有限集T1, T2, ..., Tm,其中每一个集合本身又是一棵树,并且称为根的子树(subtree)。
常见术语
-
节点(Node):树的基本组成部分,包含一个数据元素和若干指向其他节点的链接。
-
根节点(Root):树中的最顶端节点,没有父节点。
-
子节点(Child):除了根节点外,任何节点都是另一个节点的子节点。
-
父节点(Parent):给定节点的上一级节点,每个节点(除根节点外)有且仅有一个父节点。
-
兄弟节点(Sibling):具有同一父节点的节点。
-
叶节点/终端节点(Leaf):没有任何子节点的节点。
-
内部节点(Internal Node):非叶节点,至少有一个子节点。
-
路径(Path):从一个节点到另一个节点的序列,由边连接。
-
深度(Depth):从根节点到某节点的唯一路径上的边数。
-
高度(Height):从节点到其最远叶节点的最长路径上的边数。对于整个树来说,树的高度是从根节点到最远叶节点的最长路径上的边数。
-
祖先(Ancestor):从根节点到该节点路径上的所有节点。
-
子孙(Descendant):以某节点为根的子树中的所有节点。
-
森林(Forest):零个或多个不相交的树的集合。删除一棵非空树的根会得到一个森林。
-
度(Degree):一个节点的子节点数量
二叉树概念
二叉树是一种每个节点最多有两个子节点的树形数据结构,这两个子节点分别称为左子节点和右子节点。
由于二叉树的定义是递归的,所以二叉树可以为空(没有节点),或者是由一个根节点加上两棵互不相交的、分别称为左子树和右子树的二叉树组成。
二叉树结构
为了方便下面的遍历,笔者先在这里定义一个二叉树
const root = {
val:'A',
left:{
val:'B',
left:{
val:'D'
},
right:{
val:'E'
}
},
right:{
val:'C',
right:{
val:'F'
}
}
}
为了方便大家能看懂,笔者在这就不构造过于繁琐的二叉树,下面二叉树的图会稍微复杂点让大家更好的理解二叉树的遍历。
遍历二叉树
深度优先搜索
深度优先搜索(DFS)是一种遍历或搜索树或图的算法,它尽可能深地探索每个分支,直到达到搜索结构的末端然后回溯。
前序遍历
概念
前序遍历:先访问根节点,如何遍历左子树,最后遍历右子树,简单来说就是访问完根节点后递归的对左子树进行前序遍历,再递归的对右子树进行前序遍历。
图解
如上图所示,这个二叉树前序遍历的结果为ABDGHCEIF
代码
function preOrder(root){
// 退出条件
// 空树
if(!root){
return
}
// 递归式
console.log(root.val);
preOrder(root.left)
preOrder(root.right)
}
中序遍历
概念
中序遍历:先遍历左子树,然后访问根节点,最后再遍历右子树,简单来说,就是先递归的对左子树进行中序遍历,再访问根节点,最后再递归的对右子树进行中序遍历
图解
如上图所示,中序遍历的结果为GDHBAEICF
代码
function inOrder(root){
// 退出条件
// 空树
if(!root){
return
}
// 递归式
inOrder(root.left)
console.log(root.val);
inOrder(root.right)
}
后序遍历
概念
后序遍历:先遍历左子树,再遍历右子树,最后访问根节点,简单来说就是先递归的对左子树进行后序遍历,再递归的对右子树进行后序遍历,最后再访问根节点
图解
如上图所示,后序遍历的结果为GHDBIEFCA。
代码
function postOrder(root){
// 退出条件
// 空树
if(!root){
return
}
// 递归式
postOrder(root.left)
postOrder(root.right)
console.log(root.val);
}
广度优先搜索
广度优先搜索(BFS)是一种逐层扩展的算法,它从根节点开始,首先访问其所有邻接节点,然后再按同样方式访问下一层级的所有节点。
层序遍历
概念
层序遍历(Level-order Traversal)是按树的层级从上到下、同一层级从左到右依次访问节点的过程。使用队列实现,先访问根节点,然后逐层访问每个节点的左右子节点。
图解
如上图所示,层序遍历的结果为ABCDEFGHI
代码
class TreeNode{
constructor(val){
this.val=val;
this.left=this.right=null;
}
}
const root = new TreeNode("A");
root.left = new TreeNode("B");
root.right = new TreeNode("C");
root.left.left = new TreeNode("D");
root.left.right = new TreeNode("E");
root.right.right = new TreeNode("F");
console.log(root)
function levelOrderTraversal(root){
//
if(root ===null)return []
const result = []
// 根节点入队
// 借助队列 先进先出 FIFO 栈 LIFO
const queue = [root]
while(queue.length){ // 不为空
const currentNode = queue.shift() // 队头出队
result.push(currentNode.val)
if(currentNode.left!==null){
queue.push(currentNode.left)
}
if(currentNode.right!==null)
queue.push(currentNode.right)
}
return result
}
console.log(levelOrderTraversal(root))