树是一种比较高级的基础数据结构,由n
个有限节点组成的具备层次关系的集合。算法
树的定义:segmentfault
二叉树:每一个节点最多只有两个儿子节点的树。数组
满二叉树:叶子节点与叶子节点之间的高度差为0
的二叉树,即整颗树是满的,树呈满三角形结构。在国外的定义,非叶子节点儿子都是满的树就是满二叉树。咱们以国内为准。安全
彻底二叉树:彻底二叉树是由满二叉树而引出来的,设二叉树的深度为k
,除第k
层外,其余各层的节点数都达到最大值,且第k
层全部的节点都连续集中在最左边。数据结构
树根据儿子节点的多寡,有二叉树,三叉树,四叉树等,咱们这里主要介绍二叉树。并发
h≥0
的二叉树至少有h+1
个结点,好比最不平衡的二叉树就是退化的线性链表结构,全部的节点都只有左儿子节点,或者全部的节点都只有右儿子节点。h≥0
的二叉树至多有2^h+1
个节点,好比这颗树是满二叉树。n≥1
个结点的二叉树的高度至多为n-1
,由1
退化的线性链表能够反推。n≥1
个结点的二叉树的高度至少为logn
,由2
满二叉树能够反推。i
层,至多有2^(i-1)
个节点,好比该层是满的。二叉树可使用链表来实现。以下:数据结构和算法
// 二叉树 type TreeNode struct { Data string // 节点用来存放数据 Left *TreeNode // 左子树 Right *TreeNode // 右字树 }
固然,数组也能够用来表示二叉树,通常用来表示彻底二叉树。函数
对于一颗有n
个节点的彻底二叉树,从上到下,从左到右进行序号编号,对于任一个节点,编号i=0
表示树根节点,编号i
的节点的左右儿子节点编号分别为:2i+1,2i+2
,父亲节点编号为:i/2,整除操做去掉小数
。spa
如图是一颗彻底二叉树,数组的表示:code
咱们通常使用二叉树来实现查找的功能,因此树节点结构体里存放数据的Data
字段。
构建一颗树后,咱们但愿遍历它,有四种遍历方法:
先序,后序和中序遍历较简单,代码以下:
package main import ( "fmt" ) // 二叉树 type TreeNode struct { Data string // 节点用来存放数据 Left *TreeNode // 左子树 Right *TreeNode // 右字树 } // 先序遍历 func PreOrder(tree *TreeNode) { if tree == nil { return } // 先打印根节点 fmt.Print(tree.Data, " ") // 再打印左子树 PreOrder(tree.Left) // 再打印右字树 PreOrder(tree.Right) } // 中序遍历 func MidOrder(tree *TreeNode) { if tree == nil { return } // 先打印左子树 MidOrder(tree.Left) // 再打印根节点 fmt.Print(tree.Data, " ") // 再打印右字树 MidOrder(tree.Right) } // 后序遍历 func PostOrder(tree *TreeNode) { if tree == nil { return } // 先打印左子树 MidOrder(tree.Left) // 再打印右字树 MidOrder(tree.Right) // 再打印根节点 fmt.Print(tree.Data, " ") } func main() { t := &TreeNode{Data: "A"} t.Left = &TreeNode{Data: "B"} t.Right = &TreeNode{Data: "C"} t.Left.Left = &TreeNode{Data: "D"} t.Left.Right = &TreeNode{Data: "E"} t.Right.Left = &TreeNode{Data: "F"} fmt.Println("先序排序:") PreOrder(t) fmt.Println("\n中序排序:") MidOrder(t) fmt.Println("\n后序排序") PostOrder(t) }
表示将如下结构的树进行遍历:
结果以下:
先序排序: A B D E C F 中序排序: D B E A F C 后序排序 D B E F C A
层次遍历较复杂,用到一种名叫广度遍历的方法,须要使用辅助的先进先出的队列。
remove
出节点,先打印节点值,若是该节点有左子树节点,左子树入栈,若是有右子树节点,右子树入栈。核心逻辑以下:
func LayerOrder(treeNode *TreeNode) { if treeNode == nil { return } // 新建队列 queue := new(LinkQueue) // 根节点先入队 queue.Add(treeNode) for queue.size > 0 { // 不断出队列 element := queue.Remove() // 先打印节点值 fmt.Print(element.Data, " ") // 左子树非空,入队列 if element.Left != nil { queue.Add(element.Left) } // 右子树非空,入队列 if element.Right != nil { queue.Add(element.Right) } } }
完整代码:
package main import ( "fmt" "sync" ) // 二叉树 type TreeNode struct { Data string // 节点用来存放数据 Left *TreeNode // 左子树 Right *TreeNode // 右字树 } func LayerOrder(treeNode *TreeNode) { if treeNode == nil { return } // 新建队列 queue := new(LinkQueue) // 根节点先入队 queue.Add(treeNode) for queue.size > 0 { // 不断出队列 element := queue.Remove() // 先打印节点值 fmt.Print(element.Data, " ") // 左子树非空,入队列 if element.Left != nil { queue.Add(element.Left) } // 右子树非空,入队列 if element.Right != nil { queue.Add(element.Right) } } } // 链表节点 type LinkNode struct { Next *LinkNode Value *TreeNode } // 链表队列,先进先出 type LinkQueue struct { root *LinkNode // 链表起点 size int // 队列的元素数量 lock sync.Mutex // 为了并发安全使用的锁 } // 入队 func (queue *LinkQueue) Add(v *TreeNode) { queue.lock.Lock() defer queue.lock.Unlock() // 若是栈顶为空,那么增长节点 if queue.root == nil { queue.root = new(LinkNode) queue.root.Value = v } else { // 不然新元素插入链表的末尾 // 新节点 newNode := new(LinkNode) newNode.Value = v // 一直遍历到链表尾部 nowNode := queue.root for nowNode.Next != nil { nowNode = nowNode.Next } // 新节点放在链表尾部 nowNode.Next = newNode } // 队中元素数量+1 queue.size = queue.size + 1 } // 出队 func (queue *LinkQueue) Remove() *TreeNode { queue.lock.Lock() defer queue.lock.Unlock() // 队中元素已空 if queue.size == 0 { panic("over limit") } // 顶部元素要出队 topNode := queue.root v := topNode.Value // 将顶部元素的后继连接链上 queue.root = topNode.Next // 队中元素数量-1 queue.size = queue.size - 1 return v } // 队列中元素数量 func (queue *LinkQueue) Size() int { return queue.size } func main() { t := &TreeNode{Data: "A"} t.Left = &TreeNode{Data: "B"} t.Right = &TreeNode{Data: "C"} t.Left.Left = &TreeNode{Data: "D"} t.Left.Right = &TreeNode{Data: "E"} t.Right.Left = &TreeNode{Data: "F"} fmt.Println("\n层次排序") LayerOrder(t) }
输出:
层次排序 A B C D E F
我是陈星星,欢迎阅读我亲自写的 数据结构和算法(Golang实现),文章首发于 阅读更友好的GitBook。