Golang(五十四)[樹-二叉樹]

1.初始化

mkdir -p binaryTree
cd binaryTree
go mod init binaryTree
touch binaryTree.go
vim binaryTree.go

2.定義二叉樹結點結構體

// 定義二叉樹結點結構體
type BinaryTreeNode struct {
	// 數據域
	Data interface{}
	// 左子結點
	Lchild *BinaryTreeNode
	// 右子結點
	Rchild *BinaryTreeNode
}

3.根據圖創建二叉樹

在這裏插入圖片描述

// 根據圖創建二叉樹
func (node *BinaryTreeNode) CreateBinaryTree() {
	if node == nil {
		return
	}
	node1 := &BinaryTreeNode{1, nil, nil}
	node2 := &BinaryTreeNode{2, nil, nil}
	node3 := &BinaryTreeNode{3, nil, nil}
	node4 := &BinaryTreeNode{4, nil, nil}
	node5 := &BinaryTreeNode{5, nil, nil}
	node6 := &BinaryTreeNode{6, nil, nil}
	node7 := &BinaryTreeNode{7, nil, nil}

	node.Data = 0
	node.Lchild = node1

	node1.Lchild = node3
	node1.Rchild = node4
	node3.Lchild = node7

	node.Rchild = node2
	node2.Lchild = node5
	node2.Rchild = node6
}

測試

func main() {
	tree := new(BinaryTreeNode)
	tree.CreateBinaryTree()
	fmt.Println(tree) // &{0 0xc0000044c0 0xc0000044e0} // 數據,左子節點,右子節點
}

4.打印二叉樹

二叉樹的遍歷的前提都是【先左右後】

先序、中序、後序遍歷是取決於根、如果根在前就是先序DLR(Data,Lchild,Rchild),根在中間即中序LDR(Lchild,Data,Rchild)、根在後就是後序LRD(Lchild,Rchild,Data)

1.先序遍歷DLR

根、左子樹、右子樹

// 打印二叉樹---先序遍歷DLR:先根、再左、再右
func (node *BinaryTreeNode) PreOrderPrint() {
	// 容錯處理,同時也是遞歸出口
	if node == nil {
		return
	}
	// 根(Data)
	fmt.Print(node.Data, " ")
	// 左子樹(Lchild)遞歸調用
	node.Lchild.PreOrderPrint()
	// 右子樹(Rchild)遞歸調用
	node.Rchild.PreOrderPrint()
}

測試

func main() {
	tree := new(BinaryTreeNode)
	tree.CreateBinaryTree()
	fmt.Println("先序遍歷結果:")
	tree.PreOrderPrint()
}

0 1 3 7 4 2 5 6

2.中序遍歷LDR

左子樹、根、右子樹

// 打印二叉樹---中序遍歷LDR:先左、再根、再右
func (node *BinaryTreeNode) MidOrderPrint() {
	// 容錯處理,同時也是遞歸出口
	if node == nil {
		return
	}
	// 左子樹(Lchild)遞歸調用
	node.Lchild.MidOrderPrint()
	// 根(Data)
	fmt.Print(node.Data, " ")
	// 右子樹(Rchild)遞歸調用
	node.Rchild.MidOrderPrint()
}

測試

func main() {
	tree := new(BinaryTreeNode)
	tree.CreateBinaryTree()
	fmt.Println("中序遍歷結果:")
	tree.MidOrderPrint()
}

7 3 1 4 0 5 2 6

3.後序遍歷LRD

左子樹、右子樹 、根

// 打印二叉樹---後序遍歷LRD:先左、再右、再根
func (node *BinaryTreeNode) PostOrderPrint() {
	// 容錯處理,同時也是遞歸出口
	if node == nil {
		return
	}
	// 左子樹(Lchild)遞歸調用
	node.Lchild.PostOrderPrint()

	// 右子樹(Rchild)遞歸調用
	node.Rchild.PostOrderPrint()
	// 根(Data)
	fmt.Print(node.Data, " ")
}

測試

func main() {
	tree := new(BinaryTreeNode)
	tree.CreateBinaryTree()
	fmt.Println("後序遍歷結果:")
	tree.PostOrderPrint()
}

7 3 4 1 5 6 2 0

5.獲取二叉樹高度/深度

// 獲取二叉樹樹高/深度
func (node *BinaryTreeNode) TreeHeight() int {
	// 容錯處理,同時也是遞歸出口
	if node == nil {
		return 0 // 不能返回-1,否則少一個深度
	}
	// 左子樹遞歸探求深度
	lch := node.Lchild.TreeHeight()
	// 右子樹遞歸探求深度
	rch := node.Rchild.TreeHeight()
	if lch > rch {
		// 每一次遞歸深度自增
		lch++
		return lch
	} else {
		// 每一次遞歸深度自增
		rch++
		return rch
	}
}

測試

func main() {
	tree := new(BinaryTreeNode)
	tree.CreateBinaryTree()
	th := tree.TreeHeight()
	fmt.Println("二叉樹書高:", th)
}

4

6.獲取二叉樹葉子節點數

// 獲取二叉樹葉子節點數
func (node *BinaryTreeNode) LeafNumber() (number int) {
	// 容錯處理
	if node == nil {
		return
	}
	// 判斷是否是葉子節點:左子樹爲nil,同時右子樹也爲nil
	if node.Lchild == nil && node.Rchild == nil {
		number++
	}
	// 左子樹(Lchild)遞歸調用
	number += node.Lchild.LeafNumber()
	// 右子樹(Rchild)遞歸調用
	number += node.Rchild.LeafNumber()
	return
}

// 獲取二叉樹葉子節點數--指針傳參
func (node *BinaryTreeNode) LeafNumber2(number *int) {
	// 容錯處理
	if node == nil {
		return
	}
	// 判斷是否是葉子節點:左子樹爲nil,同時右子樹也爲nil
	if node.Lchild == nil && node.Rchild == nil {
		*number++
	}
	// 左子樹(Lchild)遞歸調用
	node.Lchild.LeafNumber2(number)
	// 右子樹(Rchild)遞歸調用
	node.Rchild.LeafNumber2(number)
	return
}

測試

func main() {
	tree := new(BinaryTreeNode)
	tree.CreateBinaryTree()
	number := tree.LeafNumber()
	fmt.Println("二叉樹葉子節點:", number)
	num := 0
	tree.LeafNumber2(&num)
	fmt.Println("二叉樹葉子節點:", num)
}

4

7.查找二叉樹數據

// 查找二叉樹數據
func (node *BinaryTreeNode) Search(data interface{}, b *bool) {
	if node == nil || data == nil {
		return
	}
	// 比較數據類型和值是否一致
	if reflect.DeepEqual(node.Data, data) && reflect.TypeOf(node.Data) == reflect.TypeOf(data) {
		*b = true
		return
	}
	// 左子樹(Lchild)遞歸調用
	node.Lchild.Search(data, b)
	// 右子樹(Rchild)遞歸調用
	node.Rchild.Search(data, b)
}

測試

func main() {
	tree := new(BinaryTreeNode)
	tree.CreateBinaryTree()
	number := 13
	b := false
	tree.Search(number, &b)
	fmt.Printf("二叉樹是否存在數據 %d : %v\r\n", number, b)
}

8.銷燬二叉樹

// 銷燬二叉樹
func (node *BinaryTreeNode) Destroy() {
	if node == nil {
		return
	}
	// 左子樹(Lchild)遞歸調用
	node.Lchild.Destroy()
	// 銷燬數據,促使GC工作
	node.Data = nil
	node.Lchild = nil

	// 右子樹(Rchild)遞歸調用
	node.Rchild.Destroy()
	// 銷燬數據,促使GC工作
	node.Data = nil
	node.Rchild = nil
}

測試

func main() {
	tree := new(BinaryTreeNode)
	tree.CreateBinaryTree()
	fmt.Println("銷燬前:")
	tree.PreOrderPrint()
	fmt.Println()
	tree.Destroy()
	fmt.Println("銷燬後:")
	tree.PreOrderPrint()
}

或者可以直接tree = nil

9.翻轉(滿二叉樹)

完全二叉樹

  • 除最後一層外,每一層的結點數都達到最大值。(左子、右子都不缺)
  • “滿二叉樹” 是“完全二叉樹”的特例!
    在這裏插入圖片描述

滿二叉樹

每個結點都有 左子結點、右子結點 的 二叉樹。

在這裏插入圖片描述
將創建二叉樹的7的葉子節點註釋掉形成滿二叉樹:

// 根據圖創建二叉樹
func (node *BinaryTreeNode) CreateBinaryTree() {
	if node == nil {
		return
	}
	node1 := &BinaryTreeNode{1, nil, nil}
	node2 := &BinaryTreeNode{2, nil, nil}
	node3 := &BinaryTreeNode{3, nil, nil}
	node4 := &BinaryTreeNode{4, nil, nil}
	node5 := &BinaryTreeNode{5, nil, nil}
	node6 := &BinaryTreeNode{6, nil, nil}
	// node7 := &BinaryTreeNode{7, nil, nil}

	node.Data = 0
	node.Lchild = node1

	node1.Lchild = node3
	node1.Rchild = node4
	// node3.Lchild = node7

	node.Rchild = node2
	node2.Lchild = node5
	node2.Rchild = node6
}

翻轉操作

// 二叉樹翻轉(滿二叉樹)
func (node *BinaryTreeNode) Reverse() {
	if node == nil {
		return
	}
	// 左子樹和右子樹交換位置,go的多重賦值實現
	node.Lchild, node.Rchild = node.Rchild, node.Lchild

	// 左子樹(Lchild)遞歸調用
	node.Lchild.Reverse()
	// 右子樹(Rchild)遞歸調用
	node.Rchild.Reverse()
}

測試

func main() {
	tree := new(BinaryTreeNode)
	tree.CreateBinaryTree()
	fmt.Println("翻轉前:")
	tree.PreOrderPrint()
	fmt.Println()
	tree.Reverse()
	fmt.Println("翻轉後:")
	tree.PreOrderPrint()
}

翻轉前:
0 1 3 4 2 5 6
翻轉後:
0 2 6 5 1 4 3

10.拷貝

// 拷貝二叉樹
func (node *BinaryTreeNode) Copy() *BinaryTreeNode {
	// 容錯處理
	if node == nil {
		return nil
	}
	// 左子樹(Lchild)遞歸調用,得到左子樹
	oldLChild := node.Lchild.Copy()
	// 右子樹(Rchild)遞歸調用,得到右子樹
	oldRChild := node.Rchild.Copy()

	// 創建新結點並賦值
	newBinaryTreeNode := new(BinaryTreeNode)
	newBinaryTreeNode.Data = node.Data
	newBinaryTreeNode.Lchild = oldLChild
	newBinaryTreeNode.Rchild = oldRChild

	return newBinaryTreeNode
}

測試

func main() {
	tree := new(BinaryTreeNode)
	tree.CreateBinaryTree()
	fmt.Println("原二叉樹:")
	tree.PreOrderPrint()
	fmt.Println("\r\n拷貝的二叉樹:")
	newTree := tree.Copy()
	newTree.PreOrderPrint()
	fmt.Println("\r\n修改拷貝的二叉樹,測試修改是否導致原二叉樹的變化:")
	newTree.Lchild.Data = 666
	fmt.Println("原二叉樹:")
	tree.PreOrderPrint()
	fmt.Println("\r\n拷貝並將左子樹的數值修改爲666的二叉樹:")
	newTree.PreOrderPrint()
}

原二叉樹:
0 1 3 7 4 2 5 6
拷貝的二叉樹:
0 1 3 7 4 2 5 6
修改拷貝的二叉樹,測試修改是否導致原二叉樹的變化:
原二叉樹:
0 1 3 7 4 2 5 6
拷貝並將左子樹的數值修改爲666的二叉樹:
0 666 3 7 4 2 5 6

11.完整代碼

package main

import (
	"fmt"
	"reflect"
)

// 定義二叉樹結點結構體
type BinaryTreeNode struct {
	// 數據域
	Data interface{}
	// 左子結點
	Lchild *BinaryTreeNode
	// 右子結點
	Rchild *BinaryTreeNode
}

// 根據圖創建二叉樹
func (node *BinaryTreeNode) CreateBinaryTree() {
	if node == nil {
		return
	}
	node1 := &BinaryTreeNode{1, nil, nil}
	node2 := &BinaryTreeNode{2, nil, nil}
	node3 := &BinaryTreeNode{3, nil, nil}
	node4 := &BinaryTreeNode{4, nil, nil}
	node5 := &BinaryTreeNode{5, nil, nil}
	node6 := &BinaryTreeNode{6, nil, nil}
	node7 := &BinaryTreeNode{7, nil, nil}

	node.Data = 0
	node.Lchild = node1

	node1.Lchild = node3
	node1.Rchild = node4
	node3.Lchild = node7

	node.Rchild = node2
	node2.Lchild = node5
	node2.Rchild = node6
}

// 打印二叉樹---先序遍歷DLR:先根、再左、再右
func (node *BinaryTreeNode) PreOrderPrint() {
	// 容錯處理,同時也是遞歸出口
	if node == nil {
		return
	}
	// 根(Data)
	fmt.Print(node.Data, " ")
	// 左子樹(Lchild)遞歸調用
	node.Lchild.PreOrderPrint()
	// 右子樹(Rchild)遞歸調用
	node.Rchild.PreOrderPrint()
}

// 打印二叉樹---中序遍歷LDR:先左、再根、再右
func (node *BinaryTreeNode) MidOrderPrint() {
	// 容錯處理,同時也是遞歸出口
	if node == nil {
		return
	}
	// 左子樹(Lchild)遞歸調用
	node.Lchild.MidOrderPrint()
	// 根(Data)
	fmt.Print(node.Data, " ")
	// 右子樹(Rchild)遞歸調用
	node.Rchild.MidOrderPrint()
}

// 打印二叉樹---後序遍歷LRD:先左、再右、再根
func (node *BinaryTreeNode) PostOrderPrint() {
	// 容錯處理,同時也是遞歸出口
	if node == nil {
		return
	}
	// 左子樹(Lchild)遞歸調用
	node.Lchild.PostOrderPrint()

	// 右子樹(Rchild)遞歸調用
	node.Rchild.PostOrderPrint()
	// 根(Data)
	fmt.Print(node.Data, " ")
}

// 獲取二叉樹樹高/深度
func (node *BinaryTreeNode) TreeHeight() int {
	// 容錯處理,同時也是遞歸出口
	if node == nil {
		return 0 // 不能返回-1,否則少一個深度
	}
	// 左子樹遞歸探求深度
	lch := node.Lchild.TreeHeight()
	// 右子樹遞歸探求深度
	rch := node.Rchild.TreeHeight()
	if lch > rch {
		// 每一次遞歸深度自增
		lch++
		return lch
	} else {
		// 每一次遞歸深度自增
		rch++
		return rch
	}
}

// 獲取二叉樹葉子節點數
func (node *BinaryTreeNode) LeafNumber() (number int) {
	// 容錯處理
	if node == nil {
		return
	}
	// 判斷是否是葉子節點:左子樹爲nil,同時右子樹也爲nil
	if node.Lchild == nil && node.Rchild == nil {
		number++
	}
	// 左子樹(Lchild)遞歸調用
	number += node.Lchild.LeafNumber()
	// 右子樹(Rchild)遞歸調用
	number += node.Rchild.LeafNumber()
	return
}

// 獲取二叉樹葉子節點數--指針傳參
func (node *BinaryTreeNode) LeafNumber2(number *int) {
	// 容錯處理
	if node == nil {
		return
	}
	// 判斷是否是葉子節點:左子樹爲nil,同時右子樹也爲nil
	if node.Lchild == nil && node.Rchild == nil {
		*number++
	}
	// 左子樹(Lchild)遞歸調用
	node.Lchild.LeafNumber2(number)
	// 右子樹(Rchild)遞歸調用
	node.Rchild.LeafNumber2(number)
	return
}

// 查找二叉樹數據
func (node *BinaryTreeNode) Search(data interface{}, b *bool) {
	if node == nil || data == nil {
		return
	}
	// 比較數據類型和值是否一致
	if reflect.DeepEqual(node.Data, data) && reflect.TypeOf(node.Data) == reflect.TypeOf(data) {
		*b = true
		return
	}
	// 左子樹(Lchild)遞歸調用
	node.Lchild.Search(data, b)
	// 右子樹(Rchild)遞歸調用
	node.Rchild.Search(data, b)
}

// 銷燬二叉樹
func (node *BinaryTreeNode) Destroy() {
	if node == nil {
		return
	}
	// 左子樹(Lchild)遞歸調用
	node.Lchild.Destroy()
	// 銷燬數據,促使GC工作
	node.Data = nil
	node.Lchild = nil

	// 右子樹(Rchild)遞歸調用
	node.Rchild.Destroy()
	// 銷燬數據,促使GC工作
	node.Data = nil
	node.Rchild = nil
}

// 二叉樹翻轉(滿二叉樹)
func (node *BinaryTreeNode) Reverse() {
	if node == nil {
		return
	}
	// 左子樹和右子樹交換位置,go的多重賦值實現
	node.Lchild, node.Rchild = node.Rchild, node.Lchild

	// 左子樹(Lchild)遞歸調用
	node.Lchild.Reverse()
	// 右子樹(Rchild)遞歸調用
	node.Rchild.Reverse()
}

// 拷貝二叉樹
func (node *BinaryTreeNode) Copy() *BinaryTreeNode {
	// 容錯處理
	if node == nil {
		return nil
	}
	// 左子樹(Lchild)遞歸調用,得到左子樹
	oldLChild := node.Lchild.Copy()
	// 右子樹(Rchild)遞歸調用,得到右子樹
	oldRChild := node.Rchild.Copy()

	// 創建新結點並賦值
	newBinaryTreeNode := new(BinaryTreeNode)
	newBinaryTreeNode.Data = node.Data
	newBinaryTreeNode.Lchild = oldLChild
	newBinaryTreeNode.Rchild = oldRChild

	return newBinaryTreeNode
}
func main() {
	tree := new(BinaryTreeNode)
	tree.CreateBinaryTree()
	fmt.Println("原二叉樹:")
	tree.PreOrderPrint()
	fmt.Println("\r\n拷貝的二叉樹:")
	newTree := tree.Copy()
	newTree.PreOrderPrint()
	fmt.Println("\r\n修改拷貝的二叉樹,測試修改是否導致原二叉樹的變化:")
	newTree.Lchild.Data = 666
	fmt.Println("原二叉樹:")
	tree.PreOrderPrint()
	fmt.Println("\r\n拷貝並將左子樹的數值修改爲666的二叉樹:")
	newTree.PreOrderPrint()
}

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章