基本概念
二叉树是一种常见的树形数据结构,它由一个根节点和多个子节点组成,每个节点最多有两个子节点。二叉树的两个子节点通常称为该节点的左子树和右子树,其中左子树的所有节点都比该节点的值小,而右子树的所有节点都比该节点的值大。二叉树可以应用于许多算法中,例如搜索和排序算法,因为它具有良好的平衡性质和快速的搜索和插入操作。
二叉树的应用
二叉树的应用非常广泛,其中一种常见的应用是二叉搜索树。在二叉搜索树中,每个节点的左子树都比该节点的值小,右子树都比该节点的值大,这个特性使得二叉搜索树非常适合于快速的搜索和插入操作。另外,二叉搜索树还可以用于排序算法,例如中序遍历(左子树 -> 根节点 -> 右子树)可以得到一个有序的序列。
另外,二叉树还可以用于表示表达式和语法分析等问题。例如,我们可以使用二叉树来表示一个算术表达式,其中每个节点表示一个运算符或操作数,左子树表示运算符左边的操作数,右子树表示右边的操作数。这种表示方法被称为表达式树,我们可以使用后序遍历(左子树 -> 右子树 -> 根节点)来求得算术表达式的值。
下面就是一个使用 Go 语言中的 struct 实现的二叉树结构体
// 二叉树结构体
type TreeNode struct {
Val int
Left *TreeNode
Right *TreeNode
}
遍历二叉树
遍历二叉树是指按照一定的顺序访问二叉树的所有节点。常见的遍历方式包括前序遍历、中序遍历和后序遍历。
前序遍历按照“根节点 -> 左子树 -> 右子树”的顺序访问每个节点。具体的实现方式是,首先访问根节点,然后递归地遍历左子树和右子树。前序遍历可以用于打印树的结构信息或复制二叉树。
// 前序遍历
func preorderTraversal(root *TreeNode) []int {
if root == nil {
return []int{}
}
res := []int{}
stack := []*TreeNode{root}
for len(stack) > 0 {
node := stack[len(stack)-1]
stack = stack[:len(stack)-1]
res = append(res, node.Val)
if node.Right != nil {
stack = append(stack, node.Right)
}
if node.Left != nil {
stack = append(stack, node.Left)
}
}
return res
}
中序遍历按照“左子树 -> 根节点 -> 右子树”的顺序访问每个节点。具体的实现方式是,首先递归遍历左子树,然后访问根节点,最后递归遍历右子树。中序遍历可以用于对二叉搜索树进行排序操作。
// 中序遍历
func inorderTraversal(root *TreeNode) []int {
if root == nil {
return []int{}
}
res := []int{}
stack := []*TreeNode{}
node := root
for node != nil || len(stack) > 0 {
for node != nil {
stack = append(stack, node)
node = node.Left
}
node = stack[len(stack)-1]
stack = stack[:len(stack)-1]
res = append(res, node.Val)
node = node.Right
}
return res
}
后序遍历按照“左子树 -> 右子树 -> 根节点”的顺序访问每个节点。具体的实现方式是,首先递归遍历左子树和右子树,最后访问根节点。后序遍历可以用于对表达式树进行求值操作。
// 后序遍历
func postorderTraversal(root *TreeNode) []int {
if root == nil {
return []int{}
}
res := []int{}
stack := []*TreeNode{root}
for len(stack) > 0 {
node := stack[len(stack)-1]
stack = stack[:len(stack)-1]
res = append([]int{node.Val}, res...)
if node.Left != nil {
stack = append(stack, node.Left)
}
if node.Right != nil {
stack = append(stack, node.Right)
}
}
return res
}
下面我们写个main()函数来验证下结果
func main() {
// 创建一棵二叉树
root := &TreeNode{
Val: 1,
Left: &TreeNode{
Val: 2,
Left: &TreeNode{
Val: 4,
},
Right: &TreeNode{
Val: 5,
},
},
Right: &TreeNode{
Val: 3,
},
}
// 测试前序遍历
fmt.Println(preorderTraversal(root))
// 测试中序遍历
fmt.Println(inorderTraversal(root))
// 测试后序遍历
fmt.Println(postorderTraversal(root))
}
输出结果:
[1 2 4 5 3]
[4 2 5 1 3]
[4 5 2 3 1]
基本符合预期。
除了以上三种遍历方式,还有层序遍历,按照从上到下、从左到右的顺序依次访问每个节点。层序遍历可以用于查找二叉树的深度和广度等信息。
总结
二叉树是一种常见的树形数据结构,具有良好的平衡性质和快速的搜索和插入操作。二叉树的应用非常广泛,例如二叉搜索树、表达式树等。遍历二叉树的方法包括前序遍历、中序遍历、后序遍历和层序遍历,不同的遍历方式可以用于不同的场合,例如排序、表达式求值等。