二叉树简单入门

211 阅读4分钟

基本概念

二叉树是一种常见的树形数据结构,它由一个根节点和多个子节点组成,每个节点最多有两个子节点。二叉树的两个子节点通常称为该节点的左子树和右子树,其中左子树的所有节点都比该节点的值小,而右子树的所有节点都比该节点的值大。二叉树可以应用于许多算法中,例如搜索和排序算法,因为它具有良好的平衡性质和快速的搜索和插入操作。

二叉树的应用

二叉树的应用非常广泛,其中一种常见的应用是二叉搜索树。在二叉搜索树中,每个节点的左子树都比该节点的值小,右子树都比该节点的值大,这个特性使得二叉搜索树非常适合于快速的搜索和插入操作。另外,二叉搜索树还可以用于排序算法,例如中序遍历(左子树 -> 根节点 -> 右子树)可以得到一个有序的序列。

另外,二叉树还可以用于表示表达式和语法分析等问题。例如,我们可以使用二叉树来表示一个算术表达式,其中每个节点表示一个运算符或操作数,左子树表示运算符左边的操作数,右子树表示右边的操作数。这种表示方法被称为表达式树,我们可以使用后序遍历(左子树 -> 右子树 -> 根节点)来求得算术表达式的值。

下面就是一个使用 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]

基本符合预期。

除了以上三种遍历方式,还有层序遍历,按照从上到下、从左到右的顺序依次访问每个节点。层序遍历可以用于查找二叉树的深度和广度等信息。

总结

二叉树是一种常见的树形数据结构,具有良好的平衡性质和快速的搜索和插入操作。二叉树的应用非常广泛,例如二叉搜索树、表达式树等。遍历二叉树的方法包括前序遍历、中序遍历、后序遍历和层序遍历,不同的遍历方式可以用于不同的场合,例如排序、表达式求值等。