樹,是一種非線性資料結構,樹中的元素具有明顯的層次特性。
在這種資料結構中,每個結點只有乙個前驅,卻可以有多個後繼,類似樹,只有一根主幹,卻可以有很多分支。通常我們研究的是二叉樹,即每個結點只能分一叉的樹。
二叉樹我們可以表示成下圖的樣子,實際像一棵倒過來的樹,或者像乙個樹根。其有root根節點,相連的結點構成父子關係,同乙個父親的還構成兄弟關係。
一棵樹通常按如下規則編號每個結點,根節點為1號,層數依次向下,每層從左到右,順序編號。
為了完整的描述一棵樹,除了父子結點,我們還需要知道以下幾個概念:
樹有多叉樹和二叉樹,使用得較多得是二叉樹。二叉樹是特殊的樹,有下面一些特性:
二叉樹可以通過陣列實現,也可以通過鍊錶實現。
想要使用陣列實現二叉樹,需要樹是完全二叉樹,如果不是完全二叉樹,我們可以通過補全結點使其成為完全二叉樹。
當樹成為完全二叉樹後,將其按結點順序存入陣列中,根據完全二叉樹父子結點編號之間的數學關係,我們可以輕鬆的找到每乙個結點。
這種儲存方式簡單,但是當樹很不滿時會造成許多空間浪費。
採用鏈式實現,由上面的圖可以看出,當樹是二叉樹時,鏈式實現很簡單,每個結點包含指向左右兒子的指標和自身資料即可,但當樹空缺位置很多,很不滿時,就會造成很多的指標的浪費。尤其當樹的分叉數不確定時,鏈式儲存會浪費大量空間;不過我們可以採用兄弟結點的方法解決記憶體浪費,即每個結點都有子指標和兄弟指標。
左右兒子儲存:
樹的結構遍歷一顆二叉樹時,每個結點都會訪問三次,根據輸出結點的時機不同,可以分為三種遍歷。typedef
struct
node*treenode;
前序遍歷
第一次訪問該節點時就輸出該節點,輸出的遞迴是:根節點-->左子樹-->右子樹。
中序遍歷
第二次訪問該節點時輸出該節點,輸出的遞迴是:左子樹-->根節點-->右子樹。
後序遍歷
最後一次訪問該節點時輸出該節點,輸出的遞迴是:左子樹-->右子樹-->根節點。
樹採用遞迴遍歷非常簡單,我們先看看前序遍歷的遞迴**:
void如果先輸出根節點再遍歷左子樹,再遍歷右子樹就是先序遍歷。那麼很容易理解中序遍歷就是將**行①和②對換位置,後序遍歷就是①和③調換位置。preorder(treenode root)
雖然遞迴實現很簡潔明瞭,但面試官通常喜歡要求實現非遞迴遍歷。非遞迴遍歷也有很多種方法,最常見最容易想到的就是用棧模擬遞迴。
寫法如下,都是在棧和節點指標有乙個不為空的時候迴圈,如果p不空,壓棧,並訪問左節點,如果p空,彈棧,並指向棧頂結點的右節點。後序遍歷有點不同,需要將結點進行兩次彈出,第一次彈出的時候並不真正的彈出,第二次彈出才真的彈出。
void除了以上三種,還有一種遍歷方式是層序遍歷,即從上至下逐層,每層從左至右的遍歷。層序遍歷採用佇列實現,根節點入隊,根節點出隊的同時,讓根節點的左右結點依次入隊,後面依次出隊,每個出隊的結點都將自己的左右兒子入隊,直到隊列為空,遍歷完成。preorder(treenode p)
else
}}/*************
*/void
inorder(treenode p)
else
}}/*************
*/void
postorder(treenode p)
else
else
}}}
總結:遍歷一般有兩種,dfs深度優先和bfs廣度優先,後面圖章節會講到。在樹中,前中後序遍歷屬於深度優先搜尋,層序遍歷屬於廣度優先搜尋。
樹 二叉樹 滿二叉樹 完全二叉樹 完滿二叉樹
目錄名稱作用根 樹的頂端結點 孩子當遠離根 root 的時候,直接連線到另外乙個結點的結點被稱之為孩子 child 雙親相應地,另外乙個結點稱為孩子 child 的雙親 parent 兄弟具有同乙個雙親 parent 的孩子 child 之間互稱為兄弟 sibling 祖先結點的祖先 ancesto...
二叉樹 二叉樹
題目描述 如上所示,由正整數1,2,3 組成了一顆特殊二叉樹。我們已知這個二叉樹的最後乙個結點是n。現在的問題是,結點m所在的子樹中一共包括多少個結點。比如,n 12,m 3那麼上圖中的結點13,14,15以及後面的結點都是不存在的,結點m所在子樹中包括的結點有3,6,7,12,因此結點m的所在子樹...
二叉樹基礎
二叉樹 二叉樹是一棵特殊的樹,二叉樹每個節點最多有兩個孩子結點,分別稱為左孩子和右孩子。二叉樹節點結構 二叉樹的建立 node createtree const t a,size t size,size t index,const t invilid return root 返回根節點 前序遍歷 前...