看圖輕鬆理解資料結構與演算法系列(二叉搜尋樹)

超人汪小建發表於2018-08-01

前言

推出一個新系列,《看圖輕鬆理解資料結構和演算法》,主要使用圖片來描述常見的資料結構和演算法,輕鬆閱讀並理解掌握。本系列包括各種堆、各種佇列、各種列表、各種樹、各種圖、各種排序等等幾十篇的樣子。

關於樹

對於樹的資料結構大家都瞭解,只是樹的型別有很多,所以可能又會對樹產生一種陌生感。樹其實就是由有限n(n>=1)個節點組成的一個具有層次關係的集合,它看起來像一棵倒掛的樹,所以稱之為“樹”。

樹的特點

  • 每個節點有若干個或0個子節點;
  • 根節點沒有父節點;
  • 每一個非根節點有且只有一個父節點;
  • 每個子節點可以分為多個不相交的子樹;

二叉搜尋樹

二叉搜尋樹(Binary Search Tree,簡寫BST),又稱為二叉排序樹,屬於樹的一種,通過二叉樹將資料組織起來,樹的每個節點都包含了健值 key、資料值 data、左子節點指標、右子節點指標。其中健值 key 是最核心的部分,它的值決定了樹的組織形狀;資料值 data 是該節點對應的資料,有些場景可以忽略,舉個例子,key 為身份證號而 data 為人名,通過身份證號找人名;左子節點指標指向左子節點;右子節點指標指向右子節點。

二叉搜尋樹特點

  • 左右子樹也分別是二叉搜尋樹。
  • 左子樹的所有節點 key 值都小於它的根節點的 key 值。
  • 右子樹的所有節點 key 值都大於他的根節點的 key 值。
  • 二叉搜尋樹可以為一棵空樹。
  • 一般來說,樹中的每個節點的 key 值都不相等,但根據需要也可以將相同的 key 值插入樹中。

image

插入操作

  1. 如果為空樹則將插入節點作為根節點。
  2. 如果不為空樹則從根節點開始,比較插入節點與根節點的 key 值,值相同則不做任何處理直接返回,大於則繼續比較右子節點R,小於則繼續比較左子節點L。
  3. 右子節點R與插入節點比較,插入節點的 key 值大的話則繼續往R節點的右子節點比較,小於的話則繼續往R節點左子節點比較。
  4. 以此類推不斷往下尋找,直到找到左子節點指標或右子節點指標為空的節點,將插入節點放進去。

對於下面這棵樹,插入DH

image

建立D節點並與根節點比較,

image

D 小於 E,於是往左子節點繼續比較,

image

D 大於 C,應該往右子節點方向,而此時 C 節點的右子節點指標為空,D 節點可以放置進去。

image

同樣的,對於 H 節點,先建立 H 節點並與根節點比較,

image

H 大於 E,於是往右子節點繼續比較,

image

H 大於 G,應該往右子節點方向,而此時 G 節點的右子節點指標為空,H 節點可以放置進去。

image

插入順序性

二叉搜尋樹的形狀與節點插入順序不同而可能不同,比如對於A B C D E F G H這些節點集,按E C A B D G F H順序插入則為,

image

而如果調換前面兩個節點,按照C E A B D G F H順序插入則如下圖,形狀差別還是挺大的,

image

極端情況下,按照A B C D E F G H順序插入,則為,

image

查詢操作

  1. 則從根節點開始,比較查詢節點與根節點的 key 值,值相同則表示找到該節點,直接返回,大於則繼續往右子節點R查詢,小於則繼續往左子節點L查詢。
  2. 右子節點R與查詢節點比較,查詢節點的 key 值大的話則繼續往R節點的右子節點查詢,小於的話則繼續往R節點左子節點查詢。
  3. 以此類推不斷往下尋找,直到找到節點的 key 值與查詢節點的相同,則表示查詢成功,如果最終找不到則說明不存在該節點。

對於下圖的樹查詢 key 值為“B”和“G”的節點,

image

“B”與根節點的 key 值比較,

image

“B”小於“E”,往左子節點繼續尋找,

image

“B”小於“C”,往左子節點繼續尋找,

image

“B”大於“A”,往右子節點繼續尋找,兩者相等,找到。

image

繼續查詢 key 值為“G”的節點,與根節點比較,

image

“G”大於“E”,往右子節點,兩者相等,找到。

image

刪除操作

刪除操作分三種情況進行,

  • 如果刪除的節點為葉子節點,即它的左子節點指標和右子節點指標都為空時,則可以直接刪掉該節點,並不會影響整棵樹的結構。
  • 如果刪除的節點只有一個子節點(左子節點或右子節點),則直接將子節點提升到被刪除的節點位置。
  • 如果刪除的節點有兩個子節點,此時需要找到刪除節點的中序後繼或中序前驅來填補刪除節點,中序後繼其實就是所有大於刪除節點中最小的那個,而中序前驅就是所有小於刪除節點中最大的那個,因為二叉搜尋樹經過中序遍歷後是一個遞增序列,所以後繼就是刪除節點的後面那個節點,大於且大得最少的那個,比如1 2 3 4 5中4就是3的後繼。前驅就是刪除節點前面那個節點,比如1 2 3 4 5中2就是3的前驅。

情況一

刪除“B”葉子結點,從根節點開始查詢,

image

image

image

image

找到,直接刪除,

image

情況二

假如樹的結構如下圖,現在要刪除“C”節點,

image

從根節點開始找,

image

image

找到“C”節點,直接將原來指向“C”節點的右子節點指標指向“C”的子節點,

image

最終為,

image

情況三

要在如下的樹中刪除“E”節點,

image

“E”節點存在兩個子節點,於是開始尋找“E”節點的中序前驅來替換它,前驅在左子節點“C”下最大值的那個節點,

image

要找“C”節點下最大值節點則一直往右,

image

直到不能繼續往下,即“D”節點即是前驅,用“D”節點來替換“E”節點,

image

最終實現將“E”節點刪除。

image

-------------推薦閱讀------------

我的開源專案彙總(機器&深度學習、NLP、網路IO、AIML、mysql協議、chatbot)

為什麼寫《Tomcat核心設計剖析》

我的2017文章彙總——機器學習篇

我的2017文章彙總——Java及中介軟體

我的2017文章彙總——深度學習篇

我的2017文章彙總——JDK原始碼篇

我的2017文章彙總——自然語言處理篇

我的2017文章彙總——Java併發篇


跟我交流,向我提問:

看圖輕鬆理解資料結構與演算法系列(二叉搜尋樹)

歡迎關注:

看圖輕鬆理解資料結構與演算法系列(二叉搜尋樹)

相關文章