數據結構之樹從入門到如土(一)----大話二叉樹 及GO實現

什麼是樹

    相信大家沒見過雖然可能沒見過就散機裏的樹,但是一定見過現實世界的🌲吧. 如果問你幾個樹的主要特徵,你能答上來幾個?我猜喫貨首先會想到的能喫不能喫,這樣的屬性,其實樹有很多特點 (好吧我也答不上來了),但其中比較明顯的一點是,🌲從根開始會不斷分叉,1根樹枝可能會有1個分叉但更多的是2個分叉或者以上. 抽象出來 當一個節點產生多個子節點(n > 1)
就可以成爲樹結構。計算機裏面也是如此。
大家都知道計算機裏面最基本的數據結構就是數組,最小單位是字節,佔8位。能表示0~255的數字十六進制表示0x00 ~ 0xff。
在這裏插入圖片描述
    數組是線性結構,當你要在數組裏面查找一個數是需要遍歷每一個數,當你要插隊一個一個數到這個數組時你要把後面的同學位置都往後挪移一位,所以要移動一個數到某一個數組的某一位時,假設挪到第i個位置那麼就是 nin - i 次移動。如果計算機裏面索引0 開始 就是移動 ni1n - i -1

數組和你你的閨蜜或者朋友有什麼類似之處?

    下面說說計算機裏面的另一種結構鏈表,鏈表和 數據是類似的,就是很多數組手牽手鍊接形成的.但區別在於這種聯繫不是物理上的,在計算機內部他們之間地址不是相鄰的。
數組呢就像你的朋友閨蜜你們通過感情聯繫在一起一旦成爲了朋友就是一種比較穩定的結構,。如果你要找你的朋友是不是 很容易或者是一個電話 或者是去他家 這種在抽象意義上的進和 數組查找是類似的。如果你要交一個新的朋友,那你可能得流出一定的位置給你新的朋友,如果你要刪除你的閨蜜或者朋友 但是短時間內還保留在你內心擁有最重要的位置慢慢的可能會被其他人慢慢替代 在你心中的位置變得慢慢往後移 和數組的插入有異曲同工之處,所以類似的數組在查找的時候是比較快的,但是要刪除就比較慢了。
在這裏插入圖片描述

鏈表與你同事的關係?

    上面說到了數組,下面說說鏈表 鏈表是在抽象上是關聯的,但是在物理解結構上是不相關聯的 如果對這個概念抽象的話 可以這麼理解 在某一層面相互鏈接,但在另一個層面卻不相關。上面提到了朋友,在現實生活中種種關係中還有一種,叫做同事的關係,表面上雖然你和你的同事可能關係很不錯,但如果只是同事關係說明 你們沒有同樣的價值觀,或者愛好 在這個層面上你們不相關,但是在工作中你們相互聯繫,如果新來了一個同事,你們只要保持工作中相互關聯就好了,過幾天如果人家又走了又來新的人只只要想換在工作層面關聯,對你來說也不會有太大的成本。,不像換朋友或者閨蜜那樣,那樣耗費大量的成本。但是有好處就有壞處事務永遠存在兩面性,同時畢竟只是在工作上較爲優越的 結構,但在另一個方面 在生活着 和朋友比 你去找你朋友幫忙 比較容易呢,還是 找同時幫忙比較容易呢。
所以 鏈表 和同時關係是一樣的 在物理空間層面 不關聯 在邏輯層面關聯,帶來的好處就是要插入比較快,在查找上比較慢。

    上面的比方,只是對完全沒接觸過的同學一個映像,對於各位計算機大佬來說可能是一堆廢話,哈哈。

好了下面迴歸主線: 什麼是二叉樹呢?

    如果用一句話或者一張圖表明的話,首先數 是一個抽象的 一個節點關聯 2 個或者N個字節點的結構,也就是說抽象上實現上述定義,底層可以是數組,或者鏈表。
在這裏插入圖片描述

樹的優點

發明一個數據結構肯定要用來解決問題的啊,那數結構有什麼優點嗎?
首先看看上圖的結構你可以看到,如果數是一條路的話,那你一次只能選擇,往左或者網友。冒險者現在做出你的選擇是金錢還是權利,嗯嗯 扯遠了 回到正題如果你站在A點上準備刷怪 但是 你看看 你才 11級 A地的怪 是 40 多級 只能被 怪物追着走 此時到了分叉的路上,有一個NPC少女,站在了2個路口中間,於是你急忙 走向前去詢問, 你多大 ?.. 此時 對面的NPC沒有理會你(畢竟NPC就只會說劇情相關的對話) 看看你 說了一句 您好旅行,我是喬伊,請往左邊走。
你腦子裏可能還有疑惑,但是聽到了後面的怪物的腳步身,也顧不了這麼多了。就往坐便走了,走啊走啊 剛開始還沒覺得累 但一段時間後 腳步逐漸放緩 慢慢的停了下來,渾身感覺極度疲憊,痠痛。倒了下來,等你醒來。回想起剛纔發生的事,一驚,往身後打探但似乎也沒有怪物追隨而來。 等等 本文不是小說… 迴歸正題 這時你到達了B這個節點,你一看這裏的怪物 比 A的登記相差了差不多 10級左右還是惹不起 趕緊跑 此時 又有一個 ,長得 很像剛纔遇到NPC的NPC 說了一句 您好旅行,我是喬伊的妹妹,請往左邊走。 一臉疑惑??? 不過 還是顧不上繼續往 左邊走 此時你來到了 D 這裏的怪 只有20多級 你以爲 你惹得起 結果被揍了一頓,還好等級相差 不大,於是你又來到了 路口 同時 又 有個NPC 但這一次 你主動問了一句 難道你是 喬伊妹妹的妹妹 結果出乎意料的 NPC 回答了 一句 是的旅行者,請問能有什麼幫助您嗎~~ 這一時刻,你突然 全明白了 如果往左走 遇到的怪物等級 會比當前這塊的小 所以你很果斷的 就往左走了 到達了 G 這裏的 怪物在 7 ~20 級 可以找到適合打怪升級的環境。所以你就留在 這快樂的升級了。 當然如果G 還是不適合你 的等級 G後面沒有可以打怪的節點了,
如果G還是 等級還是比較高的 但是 你放心,喬伊會自動按照你的等級幫你生成一塊適合你打怪的 地方。

好了 小說到這就結束了,讓我們來分析分析,剛纔發生了什麼?

下面開始頒獎有請
主演:A B C …節點(數據結構)
友情出演:NPC喬伊小姐們(算法)
全場最佳跑龍套:你(實際數據)

首先數據結構決定了 數據的抽象表示和 之間的對應關係,算法決定了 數據之間的運算方式。
好了 進入正題!!!
在二叉樹裏面,每個左邊的節點比上一個節點要小,右節點比較大。 那麼 你要找 一個數據,是不是每次

二叉樹基本結

type Map struct {
	Level int  //你的等級
	Left  *Map //左邊的地圖總是比當前的地圖等級低
	Right *Map //右邊的地圖總是比當前的地圖等級高
}
type BinaryTree struct {
	Root *Map //初始村
	Size int  //打怪點數量
}

func NewBinaryTree() *BinaryTree{
	bst := &BinaryTree{}
	bst.Size = 0
	bst.Root = nil
	return bst
}
//獲取刷怪點個數
func(bst *BinaryTree) GetSize() int{
	return bst.Size
}

//有沒有刷怪點
func(bst *BinaryTree) IsEmpty() bool{
	return bst.Size == 0
}

二叉樹增刪查改 之 增

下面來看看 如果 喬伊(算法)是怎麼 怎麼指引 你到 合適的地圖:
僞代碼:


動作: 找地圖(給我要被找地圖,給我你的等級)
{ 
		如果 如果左右邊沒路了{
			我自己生成一張自己等級的刷怪地圖
			總地圖數 加1
			告知 把自己地圖加入上一張地圖的左邊或右邊
		}否則{
			如果 我的等級 比 路邊 小{
				往左走
			}如果 我的等級 比 路邊 大{
				往右走
			}
		}
}
		
//把你加進地圖
//把你加進地圖
func (bst *BinaryTree)Add(data int) {
	if bst.Root == nil{
		bst.Root = &Map{data,nil,nil}
	}else{
		bst.add(bst.Root,data)
	}
}

//內部方法 給定地圖,把你 加入節點
func (bst *BinaryTree) add(n *Map,data int) *Map {
	//如果 沒有找到任何一張可以刷怪的地圖
	if n == nil{
		//想像一下 你往左走 往右走 走到盡頭了
		//找不到合適的刷怪的地圖 那麼 就 只能 自己創建一張啦
		//地圖數量 加1張
		bst.Size ++
		//如果都沒又找到 合適的 那你只能 自己創建一張了 返回給上一層遞歸調用
		return &Map{data,nil,nil}
	}else{
		//你的等級 比較小 往左走
		if data < n.Level {
			//更新 成左邊的一張地圖 再 調用 add 繼續走
			n.Left = bst.add(n.Left,data)
		//你的等級 比較高 往右走
		}else if data > n.Level {
			//更新 成右邊的一張地圖 再 調用 add 繼續走
			n.Right = bst.add(n.Right,data)
		}
	}
	//排除掉上面的情況,意味着 data 即不大於並且不小於 並且 地圖不爲空 
	//那只有等於 這種情況了 那麼就意味着找到了
	return n
}

你每走一步 會篩選掉一半的節點 如果 16 個節點 第一次 走了 剩下 8個節點 第二走完後 4個 第三次 2個 總結成數學公式就是: 2x=n2^x = n => x=log2nx = log_2^n

那麼 x=log2nx = log_2^n 就是二叉樹查找的時間複雜度了。

二叉樹增刪查改 之 查

下面是判斷 你的等級是不是 有合適的地圖 和添加是一樣的


//這個是對外部暴露的,只需要把你扔進去就好啦
func (bst *BinaryTree) IsIn(data int){
	//調用自己內部的方法 把自己存儲的地圖 傳入
	//注意IsIn 可以被其他包調用 可以理解爲java 的public 而 下面那個方法 isIn 是內部方法  private
	bst.isIn(bst.Root,data)
}

//尋找有沒有適合你的刷怪點 每次都是一樣的 往右 或往左 所以採用遞歸
func (bst *BinaryTree) isIn(node *Map,data int) bool{
	//如果走到底 沒有合適的打怪區 就返回fasle
	if node == nil {
		return false
	//如果 這塊地方 刷怪等級太高了 就往左走
	}else if node.Level > data{
		//由於走到下一章地圖 這個過程是重複的 所以調用遞歸 node.Left 把地圖改成左邊的地圖
		bst.isIn(node.Left,data)

	}else if node.Level < data{
		//由於走到下一章地圖 這個過程是重複的 所以調用遞歸 node.Left 把地圖改成右邊的地圖
		bst.isIn(node.Right,data)
	}
	// 如果 不是大於 小於 那麼只能是 等於嘍  如果 你等等級 和地圖等級一樣說明可以找到合適的刷怪點
	return true
}
找到最大或者最小的元素

由於 地圖一致往右走,一定是最大的 所以就調用遞歸往右走 就可以找到了。
最小也是一樣 一致往左走。

//找到地圖裏面的最大 這裏就很容易理解了 一直往右走 必然是 最大的 所以從
//根節點 一直往右走 就好啦
func (bst *BinaryTree) FindMax() *Map {
	return bst.findmax(bst.Root)
}

func (bst *BinaryTree) findmax(node *Map) *Map {
	//如果走到了 最後 就 返回 遞歸的結束條件
	//一般遞歸中 必須要 包含 遞歸結束的基條件
	if node.Right == nil{
		return node
	}else{
		//遞歸 當 上面那步 不能往右走了 結果會返回 函數就結束啦
		return bst.findmax(node)
	}
}

往左走的代碼 就不給出了 自己實現下吧,和上面的是一樣的。

二叉樹增刪查改 之 刪

下面要是先 遍歷而

二叉樹的前序遍歷,中序遍歷,後續遍歷

前序遍歷:
特點:先上後下,有子優先(如果有子節點 優先取遍歷子節點),先左後右。
在這裏插入圖片描述
後序遍歷: 從樹的最下面開始從左往右 。
在這裏插入圖片描述
注意 圖上的 nil節點 是爲了便於理解 ,實際上不存在。

中序遍歷:
如果你明白了 前2 中遍歷 那麼 中序遍歷 就很簡單了,中序遍歷 特點就是從大到小。
你可以想象 一個二叉樹中 最大的在右邊最小的在左邊 中間是父節點。 所以 左 父 右 依次輸出就是中序遍歷。
在這裏插入圖片描述

代碼其實很簡單 就是遞歸調用 左邊和右邊。

//中序 遍歷
func(bst *BinaryTree) InOrder(){
	bst.inorder(bst.Root)
}
func(bst *BinaryTree) inorder(node *Map){
	if node == nil{
		return
	}
	bst.inorder(node.Left)
	fmt.Println(node.Level)
	bst.inorder(node.Right)


}

//前序遍歷
func(bst *BinaryTree) PreOrder(){
	bst.preorder(bst.Root)

}
func(bst *BinaryTree) preorder(node *Map){
	if node == nil{
		return
	}
	fmt.Println(node.Level)
	bst.preorder(node.Left)
	bst.preorder(node.Right)


}

//後序遍歷
func(bst *BinaryTree) PostOrder(){

	bst.postorder(bst.Root)
}
func(bst *BinaryTree) postorder(node *Map){
	if node == nil{
		return
	}
	bst.postorder(node.Left)
	bst.postorder(node.Right)
	fmt.Println(node.Level)

}

上面最大區別 就是 println的位置 。

二叉樹增刪查改 之 刪
刪除最大最小

下面討論下 二叉樹怎麼刪除 最大和最小的元素 ,刪除最大的元素
需要2步
第一 找到最大的元素(一直往右遞歸直到找到 一個 節點沒有 右孩子爲止)
第二 如果最大元素 它有左孩子 那麼 得備份一下後 把他託付給 要刪除 的最大節點的父節的右孩子 代替要刪除的節點位置。

在這裏插入圖片描述

刪除 最大也是同理 以下給出代碼 實現:

//刪除最大
func (bst *BinaryTree) removemax(node *Map) *Map{
	if node.Right == nil{
		bst.Size --
		//備份左孩子
		leftbak := node.Left
		return leftbak
	}
	node.Right = bst.removemax(node.Right)
	return node
}


//刪除最大
func (bst *BinaryTree) RemoveMax() *Map{
	max := bst.findmax(bst.Root)
	bst.Root = bst.removemax(bst.Root)
	return max
}
//刪除最小
func (bst *BinaryTree) RemoveMin() *Map{
	min := bst.findmin(bst.Root)
	bst.Root = bst.removemin(bst.Root)
	return min
}

//刪除最小
func (bst *BinaryTree) removemin(node *Map) *Map{
	if node.Left == nil{
		//備份一下要被刪除的左孩子 它的右孩子
		rightNode := node.Right
		bst.Size --
		return rightNode
	}
	node.Left = bst.removemin(node.Left)
	return node
}

可以看到 刪除最大最小元素 還不是很困難 它只要處理一個左孩子或者右孩子 將它備份 給到 要刪除的節點的父節點就好了。

那麼要刪除任意一個元素是不是也是一樣的邏輯呢?

刪除任意數據

在這裏插入圖片描述

func (bst *BinaryTree) Remove(data int)  {
	if bst.Root == nil || bst.Size == 0{
		panic("tree empty")
	}
	 bst.Root = bst.remove(bst.Root,data)
}

func (bst *BinaryTree) remove(node *Map,data int) *Map{
	if node == nil{
		return nil
	}
	if data < node.Level{
		node.Left = bst.remove(node.Left,data)
		return node
		
	}else if data > node.Level {
		node.Right = bst.remove(node.Right,data)
		return node

	}else{
		//上面2 個else 不滿足的話 這裏一定就是 node.Level == data
		//如果左節點 爲 空的話  把 右孩子備份 返回給父節點
		//注意 遞歸調用 就好像 剝洋蔥一樣  將原來的一層一層 通過一次一次
		//遞歸來拆分 修改完後 return 每一層 組合起來

		//處理左邊爲空 和刪除最大和最小一個原理
		if node.Left == nil{
			//取出 右邊的節點
			rightbak := node.Right
			//nil解出引用 對功能不影響
			node.Right = nil
			bst.Size --
			//返回 右邊節點處理結果 給上一層 遞歸調用
			return rightbak
		}
		//處理 右邊爲空 和刪除最大和最小一個原理
		if node.Right == nil{
			leftbak := node.Left
			//nil解出引用 對功能不影響
			node.Left = nil
			//節點計數器 -1
			bst.Size --
			//返回 右邊節點處理結果 給上一層 遞歸調用
			return leftbak
		}
		//找到最大的左孩子
		rpnode := bst.findmax(node.Left)
		//刪除最大也就是上面找到的那麼節點rpnode 後返回左子樹
		rpnode.Left = bst.removemax(node.Left)
		rpnode.Right = node.Right
		//nil解出引用 對功能不影響
		node.Left = nil
		node.Right = nil
		return rpnode

	}

}
二叉樹的 基於棧的 前序遍歷

在這裏插入圖片描述
在寫這個算法前 其實我也不理解 如何實現,但是通過 一步步的推導 中 慢慢完成 這個寫法。前序遍歷 特點就是 不斷往下遍歷 的深度優先遍歷。
首先先 按照 前序遍歷的特點: 從左上 開始 到左下 壓入棧中。

//非遞歸 前序遍歷
func (bst *BinaryTree)PreOrderNoRecursion() []int{
	//創建一個 副本來保存當前節點
	mybst := bst.Root
	//新建棧
	mystack := list.New()
	//保存遍歷的結果
	res := make([]int,0)
	for mybst != nil{
		//把結果保存
		res = append(res, mybst.Level)
		//節點壓棧
		mystack.PushBack(mybst)
		//更新成左節點
		mybst = mybst.Left
	}
	fmt.Println(mystack)
	//返回結果
	return res

}

上面代碼 很簡單,就是不斷的往左遍歷 直到12的左邊 爲 nil 沒自由子節點了 就跳出循環, res 裏會存[65,57,36,27,12] 棧裏面 存的 也是 這幾個節點,
第一步已經 完成,我們只實現了前序遍歷 的深度優先 遍歷 但要 遍歷全節點 只往左是不行的,所以 既然 棧裏面進了 那要有 出 要不然 幹嘛要多一步保存在棧裏,浪費內存嗎。
往右走 只要 取出棧裏的 節點 看他 有沒有 右節點 有的話 把 當前節點指針 指向 這個 右節點,這樣 重複 第一步的過程(一直往左深度遍歷),這樣 就能遍歷所有節點了 ,是不是很神奇。
所以總結 就 2步:

  1. 往左壓棧到底(同時記錄遇到的節點的值)
  2. 出棧,把節點指針移到 右邊的節點 然後重複第一步。
func (bst *BinaryTree)PreOrderNoRecursion() []int{
	//創建一個 副本來保存當前節點
	mybst := bst.Root
	//新建棧
	mystack := list.New()
	//保存遍歷的結果
	res := make([]int,0)
	//棧裏面有數據  或者 節點不爲空
	for mystack.Len() != 0 || mybst != nil{
		//如果節點不爲空 就往左 壓棧直到 最後一個子節點
		for mybst != nil {
			//把結果保存
			res = append(res, mybst.Level)
			//節點壓棧
			mystack.PushBack(mybst)
			//更新成左節點
			mybst = mybst.Left
		}
		//上面一步 如果有壓棧數據
		if  mystack.Len()!= 0{
			//從棧裏取出節點
			tmp := mystack.Back()
			//取出 *map 類型數值
			lbst := tmp.Value.(*Map)
			//將 指針移到節點 取出節點的右邊
			mybst = lbst.Right
			//刪除當前節點
			mystack.Remove(tmp)
		}
	}

	return res

}

在這裏插入圖片描述
可以看到 其實 按照箭頭的方向 每一個都會 被壓入棧中 ,從左到右的順序。

二叉樹的 基於棧的 中序遍歷

在這裏插入圖片描述
中序遍歷 和 前序遍歷 在路徑上是一致的,只要改變記錄值的位置就可以實現。

//非遞歸 中序遍歷
func (bst *BinaryTree)InOrderNoRecursion() []int{
	mybst := bst.Root
	mystack := list.New()
	res := make([]int,0)
	for mybst != nil || mystack.Len() !=0{
		for mybst != nil{
			mystack.PushBack(mybst)
			mybst = mybst.Left
		}
		if mystack.Len() != 0{
			v := mystack.Back()
			mybst = v.Value.(*Map)
			res = append(res,mybst.Level) //區別於 前序遍歷 這句話的位置發生了改變
			mybst = mybst.Right
			mystack.Remove(v)
		}
	}

	return res

}
二叉樹的 基於棧的 後序遍歷

在這裏插入圖片描述
後序遍歷 左 右 上

關鍵一點:要在 左 右 都 遍歷到了 才能輸出 60,
所以 63應該是上一個被訪問到的, 先 壓棧 60 59 然後出棧 59 沒有子節點, 記錄 這個節點的訪問。 再 出棧 60 假設 此時 63 沒有了 是不是 後序遍歷就是 59 60 所以
第一個 條件判斷出來了 if node.right == nil 那麼 就直接 append 60 pop 60
現在 右節點 是存在的 所以 是不是 60 認爲 63 必須先被 記錄 過 才能 記錄 60 因爲後序遍歷按照左右上的順序。
那麼 第二個條件 出來了 if 60.right == 上一次遍歷的節點
那麼 現在 沒有被遍歷過 是不是先 把指針 指向這右邊的節點 ,那麼 就是 指針 = 60.right
此時 60 還沒有被記錄過 所以先只移動指針,不出棧。
接下來 訪問 63 假設 63 還有很多子節點 那麼肯定就是直接到最小子節點去,但如果63 沒有 子節點了 left 和 right == nil 了 是不是 直接記錄 然後出棧 就好了 同時記錄下 上一個被訪問節點 = 63 , 然後 再從棧裏取出 60 然後判斷下 63有沒有訪問過 如果訪問過就 記錄到 數組 然後出棧。搞明白 了 3個的結構 n個結構也是一樣的,感覺遞歸能解決的問題 循環 也是找到最小的結構去分析 就好了。
下面給出代碼:

func (bst *BinaryTree)PostOrderNoRecursion() []int{
	//創建一個 副本來保存當前節點
	mybst := bst.Root
	//新建棧
	mystack := list.New()
	//保存遍歷的結果
	res := make([]int,0)
	//記錄上一個被訪問的節點
	var PreVisited *Map

	for mystack.Len() != 0 || mybst != nil{
		//如果節點不爲空 就往左 壓棧直到 最後一個子節點
		for mybst != nil {
			//節點壓棧
			mystack.PushBack(mybst)
			//更新成左節點
			mybst = mybst.Left
		}
		v := mystack.Back()//取出棧中的節點
		top := v.Value.(*Map)
		// 三個條件 左右沒有子節點  沒有 右節點 可以出戰 右節點 被訪問過了可以出棧
		if (top.Left == nil && top.Right == nil) || top.Right == nil ||(PreVisited == top.Right) {
			//記錄後序遍歷的值
			res= append(res,top.Level)
			//把 當前節點 設置爲上一個被訪問的節點
			PreVisited = top
			//出棧
			mystack.Remove(v)
		}else{
			// 如果 右邊的節點 沒有被訪問過 就往右
			mybst = top.Right
		}

	}

	return res
}

二叉樹最小公共祖先

在這裏插入圖片描述

//二叉樹的公共祖先
//二叉樹的公共祖先
func (bst *BinaryTree) BinaryTreeAncestry(root *Map,nodeA *Map,nodeB *Map) *Map{
	//如果到底了
	if root == nil{
		return nil
	}
	//如果搜索到了 節點
	if root.Level == nodeA.Level || root.Level == nodeB.Level{
		return root
	}
	//上面 2 個條件 是 整個遞歸的結束條件

	left := bst.BinaryTreeAncestry(root.Left,nodeA,nodeB)
	right := bst.BinaryTreeAncestry(root.Right,nodeA,nodeB)
	//如果 左右 都找到了 返上回公共最小祖先
	if left != nil && right  != nil {
		return root
	}
	//往上返回右邊的值
	if left != nil{
		return left
	//往上返回左邊找到的值
	}else{
		return right
	}

}
求二叉樹的 深度

計算二叉樹深度 就是遞歸 然後 每層都返回 + 1 然後誰的深度大 就是二叉樹的深度。
在這裏插入圖片描述

//求二叉樹的深度
func (bst *BinaryTree) GetDepth() int{
	return bst.getdepth(bst.Root)
}

//求二叉樹的深度
func (bst *BinaryTree) getdepth(root *Map) int{
	//最後一層返回1
	if root.Left == nil || root.Right == nil{
		return 1
	}
	//計算左邊 和 右邊的深度
	leftlen := bst.getdepth(root.Left)
	rightlen := bst.getdepth(root.Left)
	//取 深度深度 大 的 作爲 二叉樹的深度
	if leftlen > rightlen{
		return leftlen + 1
	}else{
		return rightlen + 1
	}
}

下面是全部代碼

package main

/**
 * Created by @CaomaoBoy on 2020/3/3.
 *  email:<[email protected]>
 */

type Map struct {
	Level int  //你的等級
	Left  *Map //左邊的節點
	Right *Map //右邊的節點
}
type BinaryTree struct {
	Root *Map //初始村
	Size int  //打怪點數量
}

func NewBinaryTree() *BinaryTree{
	bst := &BinaryTree{}
	bst.Size = 0
	bst.Root = nil
	return bst
}
//獲取刷怪點個數
func(bst *BinaryTree) GetSize() int{
	return bst.Size
}

//有沒有刷怪點
func(bst *BinaryTree) IsEmpty() bool{
	return bst.Size == 0
}

//把你加進地圖
func (bst *BinaryTree)Add(data int) {
	if bst.Root == nil{
		bst.Root = &Map{data,nil,nil}
	}else{
		bst.add(bst.Root,data)
	}
}

//內部方法 給定地圖,把你 加入節點
func (bst *BinaryTree) add(n *Map,data int) *Map {
	//如果 沒有找到任何一張可以刷怪的地圖
	if n == nil{
		//想像一下 你往左走 往右走 走到盡頭了
		//找不到合適的刷怪的地圖 那麼 就 只能 自己創建一張啦
		//地圖數量 加1張
		bst.Size ++
		//如果都沒又找到 合適的 那你只能 自己創建一張了 返回給上一層遞歸調用
		return &Map{data,nil,nil}
	}else{
		//你的等級 比較小 往左走
		if data < n.Level {
			//更新 成左邊的一張地圖 再 調用 add 繼續走
			n.Left = bst.add(n.Left,data)
		//你的等級 比較高 往右走
		}else if data > n.Level {
			//更新 成右邊的一張地圖 再 調用 add 繼續走
			n.Right = bst.add(n.Right,data)
		}
	}
	return n
}

//這個是對外部暴露的,只需要把你扔進去就好啦
func (bst *BinaryTree) IsIn(data int){
	//調用自己內部的方法 把自己存儲的地圖 傳入
	//注意IsIn 可以被其他包調用 可以理解爲java 的public 而 下面那個方法 isIn 是內部方法  private
	bst.isIn(bst.Root,data)
}

//尋找有沒有適合你的刷怪點 每次都是一樣的 往右 或往左 所以採用遞歸
func (bst *BinaryTree) isIn(node *Map,data int) bool{
	//如果走到底 沒有合適的打怪區 就返回fasle
	if node == nil {
		return false
	//如果 這塊地方 刷怪等級太高了 就往左走
	}else if node.Level > data{
		//由於走到下一章地圖 這個過程是重複的 所以調用遞歸 node.Left 把地圖改成左邊的地圖
		bst.isIn(node.Left,data)

	}else if node.Level < data{
		//由於走到下一章地圖 這個過程是重複的 所以調用遞歸 node.Left 把地圖改成右邊的地圖
		bst.isIn(node.Right,data)
	}
	// 如果 不是大於 小於 那麼只能是 等於嘍  如果 你等等級 和地圖等級一樣說明可以找到合適的刷怪點
	return true
}
//找到地圖裏面的最大 這裏就很容易理解了 一直往右走 必然是 最大的 所以從
//根節點 一直往右走 就好啦
func (bst *BinaryTree) FindMax() *Map {
	return bst.findmax(bst.Root)
}

func (bst *BinaryTree) findmax(node *Map) *Map {
	//如果走到了 最後 就 返回 遞歸的結束條件
	//一般遞歸中 必須要 包含 遞歸結束的基條件
	if node.Right == nil{
		return node
	}else{
		//遞歸 當 上面那步 不能往右走了 結果會返回 函數就結束啦
		return bst.findmax(node.Right)
	}
}

//min 如上
func (bst *BinaryTree) FindMin() *Map {
	return bst.findmin(bst.Root)
}

func (bst *BinaryTree) findmin(node *Map) *Map {
	if node.Left == nil{
		return node
	}else{
		//遞歸 當 上面那步 不能往右走了 結果會返回 函數就結束啦
		return bst.findmin(node.Left)
	}
}
//中序 遍歷
func(bst *BinaryTree) InOrder(){
	fmt.Print("中序遍歷:")
	bst.inorder(bst.Root)
	fmt.Println()
}
func(bst *BinaryTree) inorder(node *Map){
	if node == nil{
		return
	}
	bst.inorder(node.Left)
	fmt.Print(node.Level," ")
	bst.inorder(node.Right)


}

//前序遍歷
func(bst *BinaryTree) PreOrder(){
	fmt.Print("前序遍歷:")
	bst.preorder(bst.Root)
	fmt.Println()

}
func(bst *BinaryTree) preorder(node *Map){
	if node == nil{
		return
	}
	fmt.Print(node.Level," ")
	bst.preorder(node.Left)
	bst.preorder(node.Right)


}

//後序遍歷
func(bst *BinaryTree) PostOrder(){
	fmt.Print("後序遍歷:")
	bst.postorder(bst.Root)
	fmt.Println()
}
func(bst *BinaryTree) postorder(node *Map){
	if node == nil{
		return
	}
	bst.postorder(node.Left)
	bst.postorder(node.Right)
	fmt.Print(node.Level," ")

}

func (bst *BinaryTree) String() string{
	var buffer bytes.Buffer
	bst.GenerateBSTstring(bst.Root,0,&buffer)
	return buffer.String()
}
func (bst *BinaryTree) GenerateBSTstring(node *Map,depth int,buffer *bytes.Buffer){
	if node == nil{
		buffer.WriteString(bst.GenerateDepthstring(depth) + "nil\n")//空節點
		return
	}
	buffer.WriteString(bst.GenerateDepthstring(depth) + strconv.Itoa(node.Level) + "\n")
	bst.GenerateBSTstring(node.Left,depth +1,buffer)
	bst.GenerateBSTstring(node.Right,depth +1,buffer)

}
func (bst *BinaryTree) GenerateDepthstring(depth int) string{
	var buffer bytes.Buffer
	for i:=0;i<depth;i++{
		buffer.WriteString("--")
	}
	return buffer.String()
}


//刪除最大
func (bst *BinaryTree) removemax(node *Map) *Map{
	if node.Right == nil{
		bst.Size --
		//備份左孩子
		leftbak := node.Left
		return leftbak
	}
	node.Right = bst.removemax(node.Right)
	return node
}


//刪除最大
func (bst *BinaryTree) RemoveMax() *Map{
	max := bst.findmax(bst.Root)
	bst.Root = bst.removemax(bst.Root)
	return max
}
//刪除最小
func (bst *BinaryTree) RemoveMin() *Map{
	min := bst.findmin(bst.Root)
	bst.Root = bst.removemin(bst.Root)
	return min
}

//刪除最小
func (bst *BinaryTree) removemin(node *Map) *Map{
	if node.Left == nil{
		//備份一下要被刪除的左孩子 它的右孩子
		rightNode := node.Right
		bst.Size --
		return rightNode
	}
	node.Left = bst.removemin(node.Left)
	return node
}

func (bst *BinaryTree) Remove(data int)  {
	if bst.Root == nil || bst.Size == 0{
		panic("tree empty")
	}
	 bst.Root = bst.remove(bst.Root,data)
}

func (bst *BinaryTree) remove(node *Map,data int) *Map{
	if node == nil{
		return nil
	}
	if data < node.Level{
		node.Left = bst.remove(node.Left,data)
		return node
		
	}else if data > node.Level {
		node.Right = bst.remove(node.Right,data)
		return node

	}else{
		//上面2 個else 不滿足的話 這裏一定就是 node.Level == data
		//如果左節點 爲 空的話  把 右孩子備份 返回給父節點
		//注意 遞歸調用 就好像 剝洋蔥一樣  將原來的一層一層 通過一次一次
		//遞歸來拆分 修改完後 return 每一層 組合起來

		//處理左邊爲空 和刪除最大和最小一個原理
		if node.Left == nil{
			//取出 右邊的節點
			rightbak := node.Right
			//nil解出引用 對功能不影響
			node.Right = nil
			bst.Size --
			return rightbak
		}
		//處理 右邊爲空 和刪除最大和最小一個原理
		if node.Right == nil{
			leftbak := node.Left
			//nil解出引用 對功能不影響
			node.Left = nil
			bst.Size --
			return leftbak
		}
		//找到最大的左孩子
		rpnode := bst.findmax(node.Left)
		//刪除最大也就是上面找到的那麼節點rpnode 後返回左子樹
		rpnode.Left = bst.removemax(node.Left)
		rpnode.Right = node.Right
		//nil解出引用 對功能不影響
		node.Left = nil
		node.Right = nil
		return rpnode

	}

}

//非遞歸 中序遍歷
func (bst *BinaryTree)InOrderNoRecursion() []int{
	mybst := bst.Root
	mystack := list.New()
	res := make([]int,0)
	for mybst != nil || mystack.Len() !=0{
		for mybst != nil{
			mystack.PushBack(mybst)
			mybst = mybst.Left
		}
		if mystack.Len() != 0{
			v := mystack.Back()
			mybst = v.Value.(*Map)
			fmt.Println(mybst.Level)
			res = append(res,mybst.Level)
			mybst = mybst.Right
			mystack.Remove(v)
		}
	}

	return res

}
func (bst *BinaryTree) StringNoRecursion() string{
	var buffer bytes.Buffer
	bst.GenerateBSTstring(bst.Root,0,&buffer)
	return buffer.String()
}
func (bst *BinaryTree) GenerateBSTstringNoRecursion(node *Map,depth int,buffer *bytes.Buffer){
	if node == nil{
		buffer.WriteString(bst.GenerateDepthstring(depth) + "nil\n")//空節點
		return
	}
	//創建一個 副本來保存當前節點
	mybst := bst.Root
	//新建棧
	mystack := list.New()
	for mystack.Len() != 0 || mybst != nil{
		//如果節點不爲空 就往左 壓棧直到 最後一個子節點
		for mybst != nil {
			depth = mystack.Len()
			buffer.WriteString(bst.GenerateDepthstring(depth) + strconv.Itoa(node.Level) + "\n")
			//節點壓棧
			mystack.PushBack(mybst)
			//更新成左節點
			mybst = mybst.Left
			depth += 1
		}
		//上面一步 如果有壓棧數據
		if  mystack.Len()!= 0{
			//從棧裏取出節點
			tmp := mystack.Back()
			//取出 *map 類型數值
			lbst := tmp.Value.(*Map)
			//將 指針移到節點 取出節點的右邊
			mybst = lbst.Right
			//刪除當前節點
			mystack.Remove(tmp)
		}
	}


}
//非遞歸 前序遍歷
func (bst *BinaryTree)PreOrderNoRecursion() []int{
	//創建一個 副本來保存當前節點
	mybst := bst.Root
	//新建棧
	mystack := list.New()
	//保存遍歷的結果
	res := make([]int,0)
	for mystack.Len() != 0 || mybst != nil{
		//如果節點不爲空 就往左 壓棧直到 最後一個子節點
		for mybst != nil {
			//把結果保存
			res = append(res, mybst.Level)
			fmt.Println(mystack.Len())
			//節點壓棧
			mystack.PushBack(mybst)
			//更新成左節點
			mybst = mybst.Left

		}
		//上面一步 如果有壓棧數據
		if  mystack.Len()!= 0{
			//從棧裏取出節點
			tmp := mystack.Back()
			//取出 *map 類型數值
			lbst := tmp.Value.(*Map)
			//將 指針移到節點 取出節點的右邊
			mybst = lbst.Right
			//刪除當前節點
			mystack.Remove(tmp)
		}
	}

	return res

}



//非遞歸 後序遍歷
func (bst *BinaryTree)PostOrderNoRecursion() []int{
	//創建一個 副本來保存當前節點
	mybst := bst.Root
	//新建棧
	mystack := list.New()
	//保存遍歷的結果
	res := make([]int,0)
	//提前被封
	var PreVisited *Map
	for mystack.Len() != 0 || mybst != nil{
		//如果節點不爲空 就往左 壓棧直到 最後一個子節點
		for mybst != nil {
			//節點壓棧
			mystack.PushBack(mybst)
			//更新成左節點
			mybst = mybst.Left
		}
		v := mystack.Back()//取出棧中的節點
		top := v.Value.(*Map)
		//[12 31 27 41 36 59 63 60 58 57 78 70 69 97 84 65]
		if (top.Left == nil && top.Right == nil) || top.Right == nil ||(PreVisited == top.Right) {
			res= append(res,top.Level)
			PreVisited = top
			mystack.Remove(v)
		}else{
			mybst = top.Right
		}
	}

	return res

}
//二叉樹的公共祖先
func (bst *BinaryTree) BinaryTreeAncestry(nodeA *Map,nodeB *Map) *Map{
	return bst.binarytreeancestry(bst.Root,nodeA,nodeB)
}
//二叉樹的公共祖先
func (bst *BinaryTree) binarytreeancestry(root *Map,nodeA *Map,nodeB *Map) *Map{
	//如果到底了
	if root == nil{
		return nil
	}
	//如果搜索到了 節點
	if root.Level == nodeA.Level || root.Level == nodeB.Level{
		return root
	}
	//上面 2 個條件 是 整個遞歸的結束條件

	left := bst.binarytreeancestry(root.Left,nodeA,nodeB)
	right := bst.binarytreeancestry(root.Right,nodeA,nodeB)
	//如果 左右 都找到了 返上回公共最小祖先
	if left != nil && right  != nil {
		return root
	}
	//往上返回右邊的值
	if left != nil{
		return left
	//往上返回左邊找到的值
	}else{
		return right
	}

}

//求二叉樹的深度
func (bst *BinaryTree) GetDepth() int{
	return bst.getdepth(bst.Root)
}

//求二叉樹的深度
func (bst *BinaryTree) getdepth(root *Map) int{
	if root.Left == nil || root.Right == nil{
		return 1
	}
	leftlen := bst.getdepth(root.Left)
	rightlen := bst.getdepth(root.Left)
	if leftlen > rightlen{
		return leftlen + 1
	}else{
		return rightlen + 1
	}

}

func main(){
	bst := NewBinaryTree()
	arr :=[16]int{65,57,84,36,58,69,97,27,41,60,70,12,31,59,63,78}
	for i:=0;i< len(arr);i++{
		bst.Add(arr[i])
	}
	//bst.PreOrder()
	//bst.InOrder()
	//bst.PostOrder()
	//fmt.Println("最大值",bst.FindMax().Level)
	//fmt.Println("最小值",bst.FindMin().Level)
	//fmt.Println(bst.String())
	//bst.RemoveMax()
	//bst.RemoveMin()
	//fmt.Println("最大值",bst.FindMax().Level)
	//fmt.Println("最小值",bst.FindMin().Level)
	//bst.Remove(45)
	//bst.InOrder()
	//bst.PostOrder()
	nodea := &Map{27,nil,nil}
	nodeb := &Map{60,nil,nil}
	fmt.Println(bst.StringNoRecursion())
	fmt.Println(bst.BinaryTreeAncestry(nodea,nodeb))
	fmt.Println(bst.GetDepth())


}

二叉樹是最基本的 樹 但是 二叉樹 如果插入 向 1 2 3 4 5 6 這樣 就會退化成鏈表了,所以 實際應用中 的解決方案 是 紅黑樹 或者AVL樹。

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