數據結構之樹-第一篇

1、二分搜索樹,數據存儲的方式是一種樹結構。而線性數據結構,把所有的數據排成一排的。爲什麼需要樹結構呢,因爲樹結構本身是一種天然的組織結構,使用樹結構非常高效。將數據使用樹結構存儲後,效率是出奇的高效。

  二分搜索樹(Binary Search Tree)具有一定的侷限性,所以引入了平衡二叉樹,平衡二叉樹包含了AVL和紅黑樹等等。當算法需要一些特殊的操作的時候,將數據組織成樹結構,會針對某一類特殊的操作,產生非常高效的結果,引入了堆和並查集,都是爲了滿足對數據的某一類特殊的操作,進行高效的處理。同時對於某些特殊的數據,很多時候,我們可以另闢蹊徑,將他們以某種形式存儲成樹結構,結果就是會對這些特殊的數據,他們所在的那個領域的問題,相應的這種解決方案,提供極其高效的結果,引入了線段樹和Trie(字典樹、前綴樹),其中線段樹主要處理線段這種特殊的數據,而Trie主要用於處理字符串這種數據。

  二叉樹,和鏈表一樣,也是一種動態數據結構,換句話說,我們不需要在創建這個數據結構的時候就去決定好這個數據結構最多可以容納多少元素,這樣的問題。我們如果要添加元素,就new一個新的空間,把它添加到數據結構中,就好了,如果要刪除元素,也是同理的。

二叉樹節點Node的定義,如下所示:

 1 package com.tree;
 2 
 3 /**
 4  * 1、二叉樹裏面的每個元素也是存儲在節點中。
 5  */
 6 public class Node<E> {
 7 
 8     private E e;//存儲元素的e
 9     // 二叉樹,兩個指向其他節點的引用。
10     private Node left;//指向左邊的引用。左孩子。
11     private Node right;//指向右邊的引用。右孩子。
12 
13 }

2、二叉樹的基本概念。

二叉樹和鏈表一樣,也具有天然的遞歸結構,在二叉樹的操作中,更加強調遞歸結構。

二叉樹中,一個節點也可以看作是二叉樹,甚至一個NULL也可以看作是二叉樹。類比鏈表中,只有一個節點,就是頭部節點可以看作是鏈表,也可以把空本身看作是一個鏈表。 

3、 二分搜索樹Binary Search Tree,首先,二分搜索樹是一顆二叉樹。

4、二分搜索樹添加元素。首先,二分搜索樹裏面一個節點都沒有,也就是說二分搜索樹的root是空的話。

4.1、演示一個複雜的二分搜索樹添加元素的過程。

此時,來了一個新的節點元素60,從根節點出發,60比41大,插入到根節點41的右子樹,就變成了60和58作比較,60比58大,就應該插入到58的右子樹去,此時58的右子樹爲空,所以60變成了58的右子樹的根節點。

此時,來了一個新的節點元素28,從根節點出發,28比41小,插入到根節點41的左子樹,就變成了28和41的左孩子22作比較,28比22大,就應該插入到22的右子樹,就變成了28和22的右孩子33做比較,28比33小,就要和33的左孩子進行比較,此時,33沒有左孩子,所以,28變成了33的左孩子。

此時,來了一個新的節點元素50,從根節點出發,50比根節點41大,插入到根節點41的右子樹,此時,50和根節點41的右孩子58比較,50比根節點41的右孩子58小,就插入到58的左子樹,此時50和根節點58的左孩子50進行比較,由於50和根節點58的左孩子(另外一個50節點)50相等,在這種情況下怎麼辦呢,在此代碼實現中實現的二分搜索樹,不做任何改變,相當於50已經存在於這個二分搜索樹當中了。即此實現的二分搜索樹不包含重複元素的。

在某些應用中,想包含重複的元素,也不難,只要改變他們的定義,比如說,讓左子樹的所有節點小於等於這個節點,或者右子樹的所有節點大於等於這個節點。

5、二分搜索樹的前序遍歷,過程如下所示。看着可能有點亂,建議自己使用debug和畫圖的方式進行理解,代碼邏輯看似簡單,實則暗藏殺機。

6、如何快速的獲取到二分搜索樹的前序遍歷,中序遍歷,後序遍歷的結果。

6.1、如何快速的獲取到二分搜索樹的前序遍歷,中序遍歷,後序遍歷的結果。

7、二分搜索樹前序遍歷的非遞歸寫法。棧這種數據結構,棧有一個非常重要的應用,就是在我們的系統中,做這種程序調用的時候,在進行子過程的調用的時候,可以把原來這個過程執行到了哪裏,給壓入系統棧中,方便在子過程調用結束之後,找回原來的那個位置。二分搜索樹可以藉助棧來記錄之後我們要訪問節點的順序。

之後,由於訪問了28這個元素,下面就要訪問28的子樹了,此時,將28元素的兩個子樹壓入棧,壓入棧的順序是先壓入右孩子,再壓入左孩子,因爲棧是後入先出的,所以要先壓入後續要訪問的節點。此時的棧頂元素16就是下一次要訪問的節點,那麼,棧頂元素16出棧,此時對16進行相應的操作。

此時,對16這個節點先壓入右孩子,再壓入左孩子,就是先壓入22,再壓入13。之後,要壓入13的右孩子和左孩子,此時,13的左右孩子都是空,所以什麼都不用壓入了。此時繼續看棧頂元素22。

此時對棧頂元素22進行相應的操作,對22進行相應的操作,此時22就訪問完了,此時壓入22的左右子樹,由於22的左右子樹都是空,所以什麼操作都不用做,此時,繼續看棧頂元素是誰,此時棧頂元素是30,此時讓棧頂元素30出棧,對它進行相應的操作,此時對30這個節點訪問完了,下面相應的將30這個節點的左右子樹壓入棧。

此時,將30這個元素的右子樹42和左子樹壓29入棧,此時對棧頂元素29進行操作,將棧頂元素出棧,對它進行相應的操作,此時29這個節點就訪問完了,之後壓入29的左右子樹,由於29的左右子樹都是空,所以什麼都不做。

此時,將棧頂元素42拿出來,進行相應的操作,此時29這個節點就訪問完了,之後壓入42的左右子樹,由於42的左右子樹都是空,所以什麼都不做。此時,再拿出棧頂元素,但是此時的棧已經空了,說明棧已經沒有再記錄要訪問任何節點了,至此,我們的整棵二分搜索樹的前序遍歷就遍歷完了。

8、層序遍歷(又稱爲廣度優先遍歷),其實非常好理解,對於這個二分搜索樹來說,每一個節點相應的就有一個深度的值,通常呢,把根節點的深度爲0相應的這個節點。所謂的層序遍歷,就是一層一層的,先遍歷到第0層的節點,再遍歷到第一層的節點,再遍歷到第二層的節點,依次類推,這種遍歷方式又稱爲廣度優先遍歷。可以理解爲逐層向下遍歷的節點,在廣度上進行拓展,而不像之前遍歷方式,順着一個枝杈向最深地方走。

之後,將根節點28的左右兩個孩子分別入隊,此時分別將左孩子16和右孩子30入隊。因爲對於隊列來說,是先到先得,或者先進先出,所以我們按照從左到右的順序進行入隊。

由於篇幅限制,接下一篇。

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