树是一种比较高级的基础数据结构,由 n 个有限节点组成的具有层次关系的集合。

树的定义:

  1. 有节点间的层次关系,分为父节点和子节点。
  2. 有唯一一个根节点,该根节点没有父节点。
  3. 除了根节点,每个节点有且只有一个父节点。
  4. 每一个节点本身以及它的后代也是一棵树,是一个递归的结构。
  5. 没有后代的节点称为叶子节点,没有节点的树称为空树。

二叉树:每个节点最多只有两个儿子节点的树。

满二叉树:叶子节点与叶子节点之间的高度差为 0 的二叉树,即整棵树是满的,树呈满三角形结构。在国外的定义,非叶子节点儿子都是满的树就是满二叉树。我们以国内为准。

完全二叉树:完全二叉树是由满二叉树而引出来的,设二叉树的深度为 k,除第 k 层外,其他各层的节点数都达到最大值,且第 k 层所有的节点都连续集中在最左边。

树根据儿子节点的多寡,有二叉树,三叉树,四叉树等,我们这里主要介绍二叉树。

一、二叉树的数学特征

  1. 高度为 h≥0 的二叉树至少有 h+1 个结点,比如最不平衡的二叉树就是退化的线性链表结构,所有的节点都只有左儿子节点,或者所有的节点都只有右儿子节点。
  2. 高度为 h≥0 的二叉树至多有 2^h+1 个节点,比如这棵树是满二叉树。
  3. 含有 n≥1 个结点的二叉树的高度至多为 n-1,由 1 退化的线性链表可以反推。
  4. 含有 n≥1 个结点的二叉树的高度至少为 logn,由 2 满二叉树可以反推。
  5. 在二叉树的第 i 层,至多有 2^(i-1) 个节点,比如该层是满的。

二、二叉树的实现

二叉树可以使用链表来实现。如下:

  1. // 二叉树
  2. type TreeNode struct {
  3. Data string // 节点用来存放数据
  4. Left *TreeNode // 左子树
  5. Right *TreeNode // 右字树
  6. }

当然,数组也可以用来表示二叉树,一般用来表示完全二叉树。

对于一棵有 n 个节点的完全二叉树,从上到下,从左到右进行序号编号,对于任一个节点,编号 i=0 表示树根节点,编号 i 的节点的左右儿子节点编号分别为:2i+1,2i+2,父亲节点编号为:i/2,整除操作去掉小数

如图是一棵完全二叉树,数组的表示:

树 - 图1

我们一般使用二叉树来实现查找的功能,所以树节点结构体里存放数据的 Data 字段。

三、遍历二叉树

构建一棵树后,我们希望遍历它,有四种遍历方法:

  1. 先序遍历:先访问根节点,再访问左子树,最后访问右子树。
  2. 后序遍历:先访问左子树,再访问右子树,最后访问根节点。
  3. 中序遍历:先访问左子树,再访问根节点,最后访问右子树。
  4. 层次遍历:每一层从左到右访问每一个节点。

先序,后序和中序遍历较简单,代码如下:

  1. package main
  2. import (
  3. "fmt"
  4. )
  5. // 二叉树
  6. type TreeNode struct {
  7. Data string // 节点用来存放数据
  8. Left *TreeNode // 左子树
  9. Right *TreeNode // 右字树
  10. }
  11. // 先序遍历
  12. func PreOrder(tree *TreeNode) {
  13. if tree == nil {
  14. return
  15. }
  16. // 先打印根节点
  17. fmt.Print(tree.Data, " ")
  18. // 再打印左子树
  19. PreOrder(tree.Left)
  20. // 再打印右字树
  21. PreOrder(tree.Right)
  22. }
  23. // 中序遍历
  24. func MidOrder(tree *TreeNode) {
  25. if tree == nil {
  26. return
  27. }
  28. // 先打印左子树
  29. MidOrder(tree.Left)
  30. // 再打印根节点
  31. fmt.Print(tree.Data, " ")
  32. // 再打印右字树
  33. MidOrder(tree.Right)
  34. }
  35. // 后序遍历
  36. func PostOrder(tree *TreeNode) {
  37. if tree == nil {
  38. return
  39. }
  40. // 先打印左子树
  41. PostOrder(tree.Left)
  42. // 再打印右字树
  43. PostOrder(tree.Right)
  44. // 再打印根节点
  45. fmt.Print(tree.Data, " ")
  46. }
  47. func main() {
  48. t := &TreeNode{Data: "A"}
  49. t.Left = &TreeNode{Data: "B"}
  50. t.Right = &TreeNode{Data: "C"}
  51. t.Left.Left = &TreeNode{Data: "D"}
  52. t.Left.Right = &TreeNode{Data: "E"}
  53. t.Right.Left = &TreeNode{Data: "F"}
  54. fmt.Println("先序排序:")
  55. PreOrder(t)
  56. fmt.Println("\n中序排序:")
  57. MidOrder(t)
  58. fmt.Println("\n后序排序")
  59. PostOrder(t)
  60. }

表示将以下结构的树进行遍历:

树 - 图2

结果如下:

  1. 先序排序:
  2. A B D E C F
  3. 中序排序:
  4. D B E A F C
  5. 后序排序
  6. D E B F C A

层次遍历较复杂,用到一种名叫广度遍历的方法,需要使用辅助的先进先出的队列。

  1. 先将树的根节点放入队列。
  2. 从队列里面 remove 出节点,先打印节点值,如果该节点有左子树节点,左子树入栈,如果有右子树节点,右子树入栈。
  3. 重复2,直到队列里面没有元素。

核心逻辑如下:

  1. func LayerOrder(treeNode *TreeNode) {
  2. if treeNode == nil {
  3. return
  4. }
  5. // 新建队列
  6. queue := new(LinkQueue)
  7. // 根节点先入队
  8. queue.Add(treeNode)
  9. for queue.size > 0 {
  10. // 不断出队列
  11. element := queue.Remove()
  12. // 先打印节点值
  13. fmt.Print(element.Data, " ")
  14. // 左子树非空,入队列
  15. if element.Left != nil {
  16. queue.Add(element.Left)
  17. }
  18. // 右子树非空,入队列
  19. if element.Right != nil {
  20. queue.Add(element.Right)
  21. }
  22. }
  23. }

完整代码:

  1. package main
  2. import (
  3. "fmt"
  4. "sync"
  5. )
  6. // 二叉树
  7. type TreeNode struct {
  8. Data string // 节点用来存放数据
  9. Left *TreeNode // 左子树
  10. Right *TreeNode // 右字树
  11. }
  12. func LayerOrder(treeNode *TreeNode) {
  13. if treeNode == nil {
  14. return
  15. }
  16. // 新建队列
  17. queue := new(LinkQueue)
  18. // 根节点先入队
  19. queue.Add(treeNode)
  20. for queue.size > 0 {
  21. // 不断出队列
  22. element := queue.Remove()
  23. // 先打印节点值
  24. fmt.Print(element.Data, " ")
  25. // 左子树非空,入队列
  26. if element.Left != nil {
  27. queue.Add(element.Left)
  28. }
  29. // 右子树非空,入队列
  30. if element.Right != nil {
  31. queue.Add(element.Right)
  32. }
  33. }
  34. }
  35. // 链表节点
  36. type LinkNode struct {
  37. Next *LinkNode
  38. Value *TreeNode
  39. }
  40. // 链表队列,先进先出
  41. type LinkQueue struct {
  42. root *LinkNode // 链表起点
  43. size int // 队列的元素数量
  44. lock sync.Mutex // 为了并发安全使用的锁
  45. }
  46. // 入队
  47. func (queue *LinkQueue) Add(v *TreeNode) {
  48. queue.lock.Lock()
  49. defer queue.lock.Unlock()
  50. // 如果栈顶为空,那么增加节点
  51. if queue.root == nil {
  52. queue.root = new(LinkNode)
  53. queue.root.Value = v
  54. } else {
  55. // 否则新元素插入链表的末尾
  56. // 新节点
  57. newNode := new(LinkNode)
  58. newNode.Value = v
  59. // 一直遍历到链表尾部
  60. nowNode := queue.root
  61. for nowNode.Next != nil {
  62. nowNode = nowNode.Next
  63. }
  64. // 新节点放在链表尾部
  65. nowNode.Next = newNode
  66. }
  67. // 队中元素数量+1
  68. queue.size = queue.size + 1
  69. }
  70. // 出队
  71. func (queue *LinkQueue) Remove() *TreeNode {
  72. queue.lock.Lock()
  73. defer queue.lock.Unlock()
  74. // 队中元素已空
  75. if queue.size == 0 {
  76. panic("over limit")
  77. }
  78. // 顶部元素要出队
  79. topNode := queue.root
  80. v := topNode.Value
  81. // 将顶部元素的后继链接链上
  82. queue.root = topNode.Next
  83. // 队中元素数量-1
  84. queue.size = queue.size - 1
  85. return v
  86. }
  87. // 队列中元素数量
  88. func (queue *LinkQueue) Size() int {
  89. return queue.size
  90. }
  91. func main() {
  92. t := &TreeNode{Data: "A"}
  93. t.Left = &TreeNode{Data: "B"}
  94. t.Right = &TreeNode{Data: "C"}
  95. t.Left.Left = &TreeNode{Data: "D"}
  96. t.Left.Right = &TreeNode{Data: "E"}
  97. t.Right.Left = &TreeNode{Data: "F"}
  98. fmt.Println("\n层次排序")
  99. LayerOrder(t)
  100. }

输出:

  1. 层次排序
  2. A B C D E F