前言
推出一個新系列,《看圖輕鬆理解資料結構和演算法》,主要使用圖片來描述常見的資料結構和演算法,輕鬆閱讀並理解掌握。本系列包括各種堆、各種佇列、各種列表、各種樹、各種圖、各種排序等等幾十篇的樣子。
關於樹
對於樹的資料結構大家都瞭解,只是樹的型別有很多,所以可能又會對樹產生一種陌生感。樹其實就是由有限n(n>=1)個節點組成的一個具有層次關係的集合,它看起來像一棵倒掛的樹,所以稱之為“樹”。
樹的特點
- 每個節點有若干個或0個子節點;
- 根節點沒有父節點;
- 每一個非根節點有且只有一個父節點;
- 每個子節點可以分為多個不相交的子樹;
二叉搜尋樹
二叉搜尋樹(Binary Search Tree,簡寫BST),又稱為二叉排序樹,屬於樹的一種,通過二叉樹將資料組織起來,樹的每個節點都包含了健值 key、資料值 data、左子節點指標、右子節點指標。其中健值 key 是最核心的部分,它的值決定了樹的組織形狀;資料值 data 是該節點對應的資料,有些場景可以忽略,舉個例子,key 為身份證號而 data 為人名,通過身份證號找人名;左子節點指標指向左子節點;右子節點指標指向右子節點。
二叉搜尋樹特點
- 左右子樹也分別是二叉搜尋樹。
- 左子樹的所有節點 key 值都小於它的根節點的 key 值。
- 右子樹的所有節點 key 值都大於他的根節點的 key 值。
- 二叉搜尋樹可以為一棵空樹。
- 一般來說,樹中的每個節點的 key 值都不相等,但根據需要也可以將相同的 key 值插入樹中。
插入操作
- 如果為空樹則將插入節點作為根節點。
- 如果不為空樹則從根節點開始,比較插入節點與根節點的 key 值,值相同則不做任何處理直接返回,大於則繼續比較右子節點R,小於則繼續比較左子節點L。
- 右子節點R與插入節點比較,插入節點的 key 值大的話則繼續往R節點的右子節點比較,小於的話則繼續往R節點左子節點比較。
- 以此類推不斷往下尋找,直到找到左子節點指標或右子節點指標為空的節點,將插入節點放進去。
對於下面這棵樹,插入D
和H
,
建立D
節點並與根節點比較,
D 小於 E,於是往左子節點繼續比較,
D 大於 C,應該往右子節點方向,而此時 C 節點的右子節點指標為空,D 節點可以放置進去。
同樣的,對於 H 節點,先建立 H 節點並與根節點比較,
H 大於 E,於是往右子節點繼續比較,
H 大於 G,應該往右子節點方向,而此時 G 節點的右子節點指標為空,H 節點可以放置進去。
插入順序性
二叉搜尋樹的形狀與節點插入順序不同而可能不同,比如對於A B C D E F G H
這些節點集,按E C A B D G F H
順序插入則為,
而如果調換前面兩個節點,按照C E A B D G F H
順序插入則如下圖,形狀差別還是挺大的,
極端情況下,按照A B C D E F G H
順序插入,則為,
查詢操作
- 則從根節點開始,比較查詢節點與根節點的 key 值,值相同則表示找到該節點,直接返回,大於則繼續往右子節點R查詢,小於則繼續往左子節點L查詢。
- 右子節點R與查詢節點比較,查詢節點的 key 值大的話則繼續往R節點的右子節點查詢,小於的話則繼續往R節點左子節點查詢。
- 以此類推不斷往下尋找,直到找到節點的 key 值與查詢節點的相同,則表示查詢成功,如果最終找不到則說明不存在該節點。
對於下圖的樹查詢 key 值為“B”和“G”的節點,
“B”與根節點的 key 值比較,
“B”小於“E”,往左子節點繼續尋找,
“B”小於“C”,往左子節點繼續尋找,
“B”大於“A”,往右子節點繼續尋找,兩者相等,找到。
繼續查詢 key 值為“G”的節點,與根節點比較,
“G”大於“E”,往右子節點,兩者相等,找到。
刪除操作
刪除操作分三種情況進行,
- 如果刪除的節點為葉子節點,即它的左子節點指標和右子節點指標都為空時,則可以直接刪掉該節點,並不會影響整棵樹的結構。
- 如果刪除的節點只有一個子節點(左子節點或右子節點),則直接將子節點提升到被刪除的節點位置。
- 如果刪除的節點有兩個子節點,此時需要找到刪除節點的中序後繼或中序前驅來填補刪除節點,中序後繼其實就是所有大於刪除節點中最小的那個,而中序前驅就是所有小於刪除節點中最大的那個,因為二叉搜尋樹經過中序遍歷後是一個遞增序列,所以後繼就是刪除節點的後面那個節點,大於且大得最少的那個,比如
1 2 3 4 5
中4就是3的後繼。前驅就是刪除節點前面那個節點,比如1 2 3 4 5
中2就是3的前驅。
情況一
刪除“B”葉子結點,從根節點開始查詢,
找到,直接刪除,
情況二
假如樹的結構如下圖,現在要刪除“C”節點,
從根節點開始找,
找到“C”節點,直接將原來指向“C”節點的右子節點指標指向“C”的子節點,
最終為,
情況三
要在如下的樹中刪除“E”節點,
“E”節點存在兩個子節點,於是開始尋找“E”節點的中序前驅來替換它,前驅在左子節點“C”下最大值的那個節點,
要找“C”節點下最大值節點則一直往右,
直到不能繼續往下,即“D”節點即是前驅,用“D”節點來替換“E”節點,
最終實現將“E”節點刪除。
-------------推薦閱讀------------
我的開源專案彙總(機器&深度學習、NLP、網路IO、AIML、mysql協議、chatbot)
跟我交流,向我提問:
歡迎關注: