樹是一種比較高級的基礎數據結構,由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。