樹
,作爲五大經典數據結構之一,有許多運用場景,比如MySQL
數據庫的B+樹
(數據結構的重要性不用強調了吧)。下面將對二叉樹
、紅黑樹
、B樹
、B+樹
等樹結構進行一些概念區分與總結,此篇博客適合新手、有一定數據結構基礎的小夥伴。
一、樹的劃分
根據子節點的個數可以劃分成N叉樹
(一般N ≥ 2
),N叉樹
擁有的特徵是每個節點至多有N個子節點。
比如,N = 2
時,稱爲二叉樹,每個節點至多隻有2個節點。
比如,N = 3
時,稱爲三叉樹,每個節點至多隻有3個節點。
特別的當N = 1
時,此時是"一叉樹"
(一般沒有這個概念,這只是我個人這麼叫),其實"一叉樹"
就是鏈表。
還有一點,樹的定義並不是很嚴謹,因爲它強調的是每個節點至多有N個子節點,那麼,如果某N叉樹
的每個節點都至多隻有N - 1
個節點,那麼它也可以稱爲N - 1叉樹
,反過來,它也可以稱爲K叉樹
(K ≥ N
)。
二、二叉樹(重點)
由上面的概念可知,當N = 2
時,稱爲二叉樹。在二叉樹還有左子樹、右子樹、左孩子、右孩子的概念。
1、二叉樹的遍歷方式
由二叉樹的結構可知,每個節點都是由左子節點、右子節點、父節點構成(有些節點的左或右子節點爲空,但不能說它沒有左或右子節點)。根據節點的訪問順序可排列出 左父右
、左右父
、父左右
、右父左
、右左父
、父右左
6種情況,但是一般要求左子節點比右子節點先訪問,因此剩下 左父右
、左右父
、父左右
三種情況。
①、中序遍歷(左父右
)
對於每個節點,先訪問它的左子樹
,再訪問本節點,最後訪問它的右子樹
,對於左子樹
、右子樹
也符合這個規定(遞歸定義)。
關於二叉樹的中序遍歷算法實現,請參考我先前的博客 LeetCode 二叉樹的中序遍歷(遞歸和非遞歸算法)
②、後序遍歷(左右父
)
對於每個節點,先訪問它的左子樹
,再訪問它的右子樹
,最後訪問本節點,對於左子樹
、右子樹
也符合這個規定(遞歸定義)。
關於二叉樹的後序遍歷算法實現,請參考我先前的博客 LeetCode 二叉樹的後序遍歷(遞歸、遞推)
③、前序遍歷(父左右
)
對於每個節點,先訪問本節點,再訪問它的左子樹
,最後訪問它的右子樹
,對於左子樹
、右子樹
也符合這個規定(遞歸定義)。
關於二叉樹的先序遍歷算法實現,請參考我先前的博客 LeetCode 二叉樹的前序遍歷(遞歸、遞推)
其實還有一種遍歷方式,層次遍歷,即按層訪問二叉樹。請參考我先前的博客 LeetCode 二叉樹的層次遍歷
2、二叉樹的特例
①、二叉搜索樹
二叉搜索樹
的定義是對於某二叉樹的每個節點b
,它的左子樹A
的所有節點的值都小於節點b
的值,它的右子樹B
的所有節點的值都大於節點b
的值,並且左子樹A
、右子樹B
同樣符合這個定義(遞歸定義)。
對於值爲10的節點,左子樹的所有節點的值 < 10,右子樹的所有節點的值 > 10,並且左子樹、右子樹中的所有節點同樣 左 < 父 < 右
的定義。
二叉搜索樹
的定義簡化一下就是左 < 父 < 右
規則,還記先前介紹的中序遍歷麼,是不是發現了什麼,中序遍歷順序不也是如此麼。其實二叉搜索樹的變現特徵就是中序遍歷得到的序列遞增
。(可能會有小夥伴問,爲什麼要引入這個特性呢?答案是查找方便。如果我們需要你在二叉樹中查target = 7
這個值是否存在,從根節點入手,如果target = root
,查找成功,停止查找;若target < root
,轉到左子樹
,否則target > root
,轉到右子樹
。)
②、平衡二叉樹
平衡二叉樹
定義是某二叉樹的左、右
兩個子樹的 高度差的絕對值不超過1,並且左、右
兩個子樹也都是一棵平衡二叉樹
(遞歸定義)。
那麼問題來了,爲啥又要引入平衡
這個概念呢?
可以看出平衡
主要是限制樹的整體高度
,將左、右子樹
的高度差縮減到1之內。
③、平衡二叉搜索樹(AVL樹、紅黑樹)
平衡二叉搜索樹
,顧名思義,是二叉樹同時滿足平衡二叉樹
以及二叉搜索樹
的定義,即二叉樹不但中序遍歷爲遞增序列,並且樹還平衡。
問題又來了,引入平衡二叉搜索樹
又是爲啥目的捏?看完下圖你就明白了。
上圖左邊的樹是平衡二叉搜索樹
,右邊是一顆非平衡的二叉搜索樹
。如果給你這兩顆樹,讓你搜索target = 15
,你會選擇那一顆?你肯定會選擇左邊的吧,因爲它看起來更矮一些,根據上面介紹的二叉搜索樹的查找規則,我們每次都大概能排除掉剩餘節點中的一半(最優的情況下,搜索複雜度爲log2n)。
AVL樹
得名於它的發明者G. M. Adelson-Velsky
和E. M. Landis
,其實它就是上面介紹的平衡二叉搜索樹
。
紅黑樹
由於平衡二叉搜索樹
的定義比較苛刻,實際過程中生成並維持一顆平衡二叉搜索樹
是比較複雜的,而紅黑樹
放寬了平衡
條件的限制,引入弱平衡
的概念。
紅黑樹的定義:
1.節點是紅色或黑色。
2.根是黑色。
3.所有葉子都是黑色(葉子是NIL節點)。
4.每個紅色節點必須有兩個黑色的子節點。(從每個葉子到根的所有路徑上不能有兩個連續的紅色節點。)
5.從任一節點到其每個葉子的所有簡單路徑都包含相同數目的黑色節點(簡稱黑高)。
關於紅黑樹的更多維護細節,請參考我的博客 數據結構之紅黑樹(還在爲看不懂紅黑樹而煩惱嗎?別再翻了,此篇足矣~)
三、多叉樹
根據先前樹的劃分,對於多叉樹也有父節點、子節點、兄弟節點的概念。
1、Trie樹(前綴樹)
Trie樹
其實就是前綴樹
,運用於大量字符串的存放。
關於Trie樹的運用,請參考我先前的博客 LeetCode 前綴和後綴搜索(前綴樹)
其實Trie樹
的關鍵就是把每個字符串的前綴進行合併
,也稱前綴樹
。
在Trie樹中,每個節點的子節點可能有26個,因爲對於每個字符串的下一個字符可能是a ~ z中的任意字母。並且26個子節點需要保持有序。
2、B樹
B樹
是多路搜索樹,對於樹中的非葉子節點,如果放了m
個關鍵字,就同時需要放m+1
個指向子節點的指針,根節點的子節點數爲[2, N]
,其他節點的子節點數爲[N/2,N]
。並且所有關鍵字在整顆樹中只出現一次,非葉子結點可以命中所有葉子結點位於同一層。
3、B+樹
B+樹
是在B樹
上進行改造升級,把葉子節點層串成一個鏈表,並且父節點修改爲每個子節點關鍵字序列的最大值。
MySQL
數據庫的表結構底層數據存儲用的就是B+
樹。
4、B*樹
B*樹
是在B+
樹的基礎上再次進行升級,把非葉子節點層也用串成鏈表。
個人總結:AVL樹
、紅黑樹
都是二叉樹
,並且是平衡二叉搜索樹
,Trie樹
、B樹
、B+樹
、B*樹
都是多叉樹
,並且是多叉搜索樹
。一般編程算法題中,二叉樹
使用的較多。對於多叉樹
,除非是專業的數據結構設計人員,用的是比較少的,但是這並不說明多叉樹
沒有用武之地,文章中多次提及MySQL數據庫中的表中的數據就是用B+樹存儲。(本博客多叉樹用的篇幅很少,但是B樹
、B+樹
等多叉樹
的插入、刪除節點是比較複雜的,可能會涉及到節點的拆分、合併,但是博主能力有限啊)
以上就是數據結構之二叉樹、紅黑樹、B樹、B+樹、Trie樹淺析的主要內容。
已更新二叉搜索樹、紅黑樹維護博客,歡迎閱讀。
數據結構之二叉搜索樹詳解(附C++代碼實現查找、插入、刪除操作)
數據結構之紅黑樹(還在爲看不懂紅黑樹而煩惱嗎?別再翻了,此篇足矣~)