前言
推出一個新系列,《看圖輕鬆理解資料結構和演算法》,主要使用圖片來描述常見的資料結構和演算法,輕鬆閱讀並理解掌握。本系列包括各種堆、各種佇列、各種列表、各種樹、各種圖、各種排序等等幾十篇的樣子。
2-3樹
2-3樹,是最簡單的B-樹,其中2、3主要體現在每個非葉子節點都有2個或3個子節點,B-樹即是平衡樹,平衡樹是為了解決不平衡樹查詢效率問題,常見的二叉平衡書有AVL樹,它雖然提高了查詢效率,但是插入操作效率不高,因為它需要再每次插入節點後維護樹的平衡,而為了解決查詢效率同時有兼顧插入效率,於是提出了2-3樹。
2-3樹特點
- 2-3樹是一棵平衡樹,但不是二叉平衡樹。
- 對於高度相同的2-3樹和二叉樹,2-3樹的節點數要大於滿二叉樹,因為有些節點可能有三個子節點。
- 2-3樹可以是一棵空樹。
- 對於2節點來說,該節點儲存了一個key及對應的value,除此之外還儲存了指向左右兩邊的子節點,子節點也是一個2-3節點,左子節點所有值小於key,右子節點所有值大於key。
- 對於3節點來說,該節點儲存了兩個key及對應的value,除此之外還儲存了指向左中右三個方向的子節點,子節點也是一個2-3節點,左子節點的所有值小於兩個key中較小的那個,中節點的所有值在兩個key值之間,右子節點大於兩個key中較大的那個。
- 對2-3樹進行中序遍歷能得到一個排好序的序列。
插入操作
剛開始是空樹,插入節點“A”,建立根節點,
插入節點“B”,從根節點開始尋找存放的節點位置,與“A”節點合併後放到同一個葉子上,此時該葉子只包含“AB”兩個專案,無需分裂,
繼續插入節點“C”,從根節點開始尋找存放的節點位置,找到“AB”葉子節點,將其放進去,
但此時該葉子節點包含了“ABC”三個專案,需要將該節點進行分裂操作,分裂的具體過程如下,找到該節點三個專案中中間大的項,
上移成為最小項和最大項的父節點,而最小項作為中間項的左子節點,最大項作為中間項的右子節點。
繼續插入“D”節點,從根節點開始尋找,
大於“B”,所以往右,
找到“C”節點,併合併到該葉子節點,該節點只有兩個專案,不必分裂。
繼續插入“E”,查詢到“CD”葉子節點,放入“E”節點後發現該葉子節點有三個專案,需要分裂,
將中間項提升到父節點,其餘兩項分裂成兩個子節點,“D”上升到父節點後存放在右邊,而且父節點只有兩個專案,不必再繼續分裂。
繼續插入“F”節點,
往下看連續分裂兩次的情況,繼續插入“G”節點,大於“B”,繼續比較,
大於“D”,往右子節點,
到右子節點後與“F”比較,發現大於“F”,放到右邊,
發現“EFG”葉子節點有三個專案,必須分裂,
將中間項“F”提升到父節點,“E”和“G”左右兩項分別稱為左右子節點,
提升到父節點後發現父節點包含了“BDF”三項,也需要分裂,於是準備將中間項“F”提升,
發現“BDF”節點本來屬於根節點,那麼分裂後就沒有根節點了,於是需要建立一個新節點作為根節點,即提升的“D”節點作為新的根節點。“B”和“F”左右兩項分別作為左右子節點。
總結起來就是:一個節點插入到一棵2-3樹中,先尋找該節點應該落到哪個葉子節點上,注意一定是在葉子節點。將新節點作為一個新項加入到葉子節點中,此時如果該節點只有兩個專案,則完成插入操作。但如果該節點有三個專案,則需要進行分裂操作,左中右三項按大小排序,將中間項提升到父節點中,而左右兩項作為左右子節點,然後可能還沒完,因為父節點上可能又包含了三個專案,如果是這樣還得做分裂操作,一直遞迴到父節點只包含一個或兩個專案。
查詢
2-3樹的查詢與二叉樹的查詢類似,從根節點開始比較,如果相等則查詢成功,否則往下繼續查詢,對於只有兩個子節點的節點則在其左右子樹中遞迴查詢,而對於有三個子節點的節點則需要在左中右子樹中遞迴查詢。
如下2-3樹,查詢“C”節點,
從根節點開始,與“D”比較,“C”小於“D”則往左,
繼續與“B”比較,“C”大於“B”則往右,
“C”與“C”相等,找到。
如果要查詢“H”節點,從根節點開始,“H”大於“D”則往右,
在“FH”節點中逐個項比較,先跟“F”項比較,“H”大於“F”,繼續比較下一項,
“H”等於“H”,找到,在“FH”節點中找到“H”。
如果要查詢“G”節點,從根節點開始,“G”大於“D”則往右,
“G”與“FH”節點逐個比較,大於“F”,繼續比較下一項,
“G”小於“H”,即“G”間於“F”和“H”之間,於是往中間,
“G”等於“G”,找到。
-------------推薦閱讀------------
我的開源專案彙總(機器&深度學習、NLP、網路IO、AIML、mysql協議、chatbot)
跟我交流,向我提問:
歡迎關注: