体育外围

体育外围
分享体育外围官网與網絡優惠

圖解:數據結構中的 6 種樹,你心中有數嗎?

據結構這門課程是計算機相關專業的基礎課,數據結構指的是數據在計算機中的存儲、組織方式。

我們在學習數據結構時候,會遇到各種各樣的基礎數據結構,比如堆棧、隊列、數組、鏈表、樹…這些基本的數據結構類型有各自的特點,不同數據結構適用于解決不同場景下的問題。

樹形結構相比數組、鏈表、堆棧這些數據結構來說,稍微復雜一點點,但樹形結構可以用于解決很多實際問題,因為現實世界事物之間的關系往往不是線性關聯的,而「樹」恰好適合描述這種非線性關系。

今天就帶大家一起學習下,數據結構中的各種「樹」,這也是面試中經常考察的內容,手撕二叉樹是常規套路,對候選人也很有區分度,學完這篇文章,相信大家都會心中有「樹」了。

從樹說起

什么是樹?現實中的樹大家都見過,在數據結構中也有樹,此樹非彼樹,不過數據結構的樹和現實中的樹在形態上確實有點相像。

樹是非線性的數據結構,用來模擬具有樹狀結構性質的數據集合,它是由n個有限節點組成的具有層次關系的集合。在數據結構中樹是非線性數據結構,那我們先來了解下,什么是線性與非線性數據結構?

線性結構

「線性結構」是一個有序數據元素的集合。其中數據元素之間的關系是一對一的關系,即除了第一個和最后一個數據元素之外,其它數據元素都是首尾相接的,常用的線性結構有:線性表,棧,隊列,雙端隊列,數組,串。

可以想象,所謂的線性結構數據組織形式,就像一條線段一樣筆直,元素之間首尾相接。比如現實中的火車進站、食堂打飯排隊的隊列。

非線性結構

簡而言之,線性結構的對立面就是「非線性結構」

線性結構中節點是首位相接一對一關系,在樹結構中節點之間不再是簡單的一對一關系,而是較為復雜的一對多的關系,一個節點可以與多個節點發生關聯,樹是一種層次化的數據組織形式,樹在現實中是可以找到例子的,比如現實中的族譜,親戚之間的關系是層次關聯的樹形關系。

數據結構中的「樹」的名字由來,是因為如果把節點之間的關系直觀展示出來,由于長得和現實世界中的樹很像,由此得名。如圖:

樹的關鍵概念

人們對樹形結構的研究比較深入,為了方便研究樹的各種性質,抽象出了一些樹相關的概念,以便清晰簡潔的描述一顆樹。下面幾個基礎概念必須了解,否則你當你刷LeetCode樹相關題目時候,或者面試官向你描述問題時,你會連題目都看不懂什么意思。

先來上一個圖解,下面的術語和概念對照著看,更容易理解。

?

什么是節點的度?

?

度很好理解,直觀來說,數一下節點有幾個分叉就說這個節點的度是多少。

?

什么是根節點?

?

在一顆樹形結構中,最頂層的那個節點就是根節點了,所有的子節點都源自它發散開來。

?

什么是父節點?

?

樹的父子關系和現實中很相似,若一個節點含有子節點,則這個節點稱為其子節點的父節點。

?

什么是葉子節點?

?

直觀來看葉子節點都位于樹的最底層,就是沒有分叉的節點,嚴格的定義是度為 0 的節點叫葉子節點。

?

什么是節點的高度?

?

高度是從葉子節點開始「自底向上」逐層累加的路徑長度,樹葉的高度為 0(有些書上也說是 0,不用糾結)

?

什么是節點的深度?

?

深度是從根節點開始「自頂向下」逐層累加的路徑長度,根的深度為1(有些書上也說是 0,問題不大)

小技巧:高度和深度,一個從下往上數,一個從上往下數。

樹的特點

樹形數據結構,具有以下的結構特點:

  • 每個節點都只有有限個子節點或無子節點;
  • 沒有父節點的節點稱為根節點;
  • 每一個非根節點有且只有一個父節點;
  • 除了根節點外,每個子節點可以分為多個不相交的子樹;
  • 樹里面沒有環路,意思就是從一個節點出發,除非往返,否則不能回到起點。

二叉樹

有了前面「樹」的基礎鋪墊,二叉樹是一種特殊的樹,還記上面我們學過「節點的度」嗎?二叉樹中每個節點的度不大于 2 ,即它的每個節點最多只有兩個分支,通常稱二叉樹節點的左右兩個分支為左右子樹。

二叉樹是很多其他樹型結構的基礎結構,比如下面要講的 AVL 樹、二叉查找樹,他們都是由二叉樹增加一些約束條件進化而來。

三種遍歷方式

二叉樹的遍歷就是逐個訪問二叉樹節點的數據,常見的二叉樹遍歷方式有三種,分別是前中后序遍歷,初學者分不清這幾個順序的差別。

「有個簡單的記憶方式,這里的「前中后」都是對于根節點而言」

先訪問根節點后訪問左右子樹的遍歷方式是前序遍歷,先訪問左右子樹最后訪問根節點的遍歷方式是后序遍歷,先訪問左子樹再訪問根節點最后訪問右子樹的遍歷方式是中序遍歷,下面詳細說明:

前序遍歷

遍歷順序是根節點->左子樹->右子樹

遍歷的得到的序列是:1 2 4 5 3 6 7

中序遍歷

遍歷順序是左子樹->根節點->右子樹

遍歷的得到的序列是:4 2 5 1 6 3 7

后序遍歷

遍歷順序是左子樹->右子樹->根節點

遍歷的得到的序列是:4 5 2 6 7 3 1

二叉查找樹

由于最基礎的二叉樹節點是無序的,想象一下如果在二叉樹中查找一個數據,最壞情況可能要要遍歷整個二叉樹,這樣的查找效率是非常低下的。

由于基礎二叉樹不利于數據的查找和插入,因此我們有必要對二叉樹中的數據進行排序,所以就有了「二叉查找樹」,可以說這種樹是為了查找而生的二叉樹,有時也稱它為「二叉排序樹」,都是同一種結構,只是換了個叫法。

查找二叉樹理解了也不難,簡單來說就是二叉樹上所有節點的,左子樹上的節點都小于根節點,右子樹上所有節點的值都大于根節點。

這樣的結構設計,使得查找目標節點非常方便,可以通過關鍵字和當前節點的對比,很快就能知道目標節點在該節點的左子樹還是右子樹上,方便在樹中搜索目標節點。

如果對排序二叉樹執行中序遍歷,因為中序遍歷的順序是:左子樹->根節點->右子樹,最終可以得到一個節點值的有序列表。

舉個栗子:對上圖的排序二叉樹執行中序遍歷,我們可以得到一個有序序列:1 2 3 4 5 6 7

查詢效率

二叉查找樹的查詢復雜度取決于目標節點的深度,因此當節點的深度比較大時,最壞的查詢效率是O(n),其中n是樹中的節點個數。

實際應用中有很多改進版的二叉查找樹,目的是盡可能使得每個節點的深度不要過深,從而提高查詢效率。比如AVL樹和紅黑樹,可以將最壞效率降低至O(log n),下面我們就來看下這兩種改進的二叉樹。

AVL樹

AVL 也叫平衡二叉查找樹。AVL 這個名字的由來,是它的兩個發明者G. M. Adelson-Velsky 和 Evgenii Landis 的縮寫,AVL最初是他們兩人在1962 年的論文「An algorithm for the organization of information」中提出來一種數據結構。

定義

AVL 樹是一種平衡二叉查找樹,二叉查找樹我們已經知道,那平衡是什么意思呢?

我們舉個天平的例子,天平兩端的重量要差不多才能平衡,否則就會出現向一邊傾斜的情況。把這個概念遷移到二叉樹中來,根節點看作是天平的中點,左子樹的高度放在天平左邊,右子樹的高度放在天平右邊,當左右子樹的高度相差「不是特別多」,稱為是平衡的二叉樹。

AVL樹有更嚴格的定義:在二叉查找樹中,任一節點對應的兩棵子樹的最大高度差為 1,這樣的二叉查找樹稱為平衡二叉樹。其中左右子樹的高度差也有個專業的叫法:平衡因子。

AVL樹的旋轉

一旦由于插入或刪除導致左右子樹的高度差大于1,此時就需要旋轉某些節點調整樹高度,使其再次達到平衡狀態,這個過程稱為旋轉再平衡。

保持樹平衡的目的是可以控制查找、插入和刪除在平均和最壞情況下的時間復雜度都是O(log n),相比普通二叉樹最壞情況的時間復雜度是 O(n) ,AVL樹把最壞情況的復雜度控制在可接受范圍,非常合適對算法執行時間敏感類的應用。

B樹

B樹是魯道夫·拜爾(Rudolf Bayer)1972年在波音研究實驗室(Boeing Research Labs)工作時發明的,關于B樹名字的由來至今是個未解之謎,有人猜是Bayer的首字母,也有人說是波音實驗室(Boeing Research Labs)的Boeing首字母縮寫,雖然B樹這個名字來源撲朔迷離,我們心里也沒點 B 樹,但不影響今天我們來學習它。

定義

一個?m?階的B樹是一個有以下屬性的樹

  1. 每一個節點最多有?m?個子節點
  2. 每一個非葉子節點(除根節點)最少有 ?m/2? 個子節點,?m/2?表示向上取整。
  3. 如果根節點不是葉子節點,那么它至少有兩個子節點
  4. 有?k?個子節點的非葉子節點擁有?k?? 1 個鍵
  5. 所有的葉子節點都在同一層

如果之前不了解,相信第一眼看完定義肯定是蒙圈,不過多看幾遍好好理解一下就好了,畫個圖例,對照著看看:

圖3

特點

  • B樹是所有節點的平衡因子均等于0的多路查找樹(AVL樹是平衡因子不大于 1 的二路查找樹)。

  • B 樹節點可以保存多個數據,使得 B 樹可以不用像 AVL 樹那樣為了保持平衡頻繁的旋轉節點。

  • B樹的多路的特性,降低了樹的高度,所以B樹相比于平衡二叉樹顯得矮胖很多。

  • B樹非常適合保存在磁盤中的數據讀取,因為每次讀取都會有一次磁盤IO,高度降低減少了磁盤IO的次數。

B樹常用于實現數據庫索引,典型的實現,MongoDB索引用B樹實現,MySQL的Innodb 存儲引擎用B+樹存放索引。

說到B樹不得不提起它的好兄弟B+樹,不過這里不展開細說,只需知道,B+樹是對B樹的改進,數據都放在葉子節點,非葉子節點只存數據索引。

紅黑樹

紅黑樹也是一種特殊的「二叉查找樹」。

到目前為止我們學習的 AVL 樹和即將學習的紅黑樹都是二叉查找樹的變體,可見二叉查找樹真的是非常重要基礎二叉樹,如果忘了它的定義可以先回頭看看。

特點

紅黑樹中每個結點都被標記了紅黑屬性,紅黑樹除了有普通的「二叉查找樹」特性之外,還有以下的特征:

  1. 節點是紅色或黑色。
  2. 根是黑色。
  3. 所有葉子都是黑色(葉子是NIL節點)。
  4. 每個紅色節點必須有兩個黑色的子節點。(從每個葉子到根的所有路徑上不能有兩個連續的紅色節點。)
  5. 從任一節點到其每個葉子的所有簡單路徑都包含相同數目的黑色節點。

這些性質有興趣可以自行研究,不過,現在你只需要知道,這些約束確保了紅黑樹的關鍵特性:從根到葉子的最長的可能路徑不多于最短的可能路徑的兩倍長。

紅黑樹

而節點的路徑長度決定著對節點的查詢效率,這樣我們確保了,最壞情況下的查找、插入、刪除操作的時間復雜度不超過O(log n),并且有較高的插入和刪除效率。

應用場景

紅黑樹在實際應用中比較廣泛,有很多已經落地的實踐,比如學習C++的同學都知道會接觸到 STL 標準庫,而STL容器中的map、set、multiset、multimap 底層實現都是基于紅黑樹。

再比如,Linux內核中也有紅黑樹的實現,Linux系統在實現EXT3文件系統、虛擬內存管理系統,都有使用到紅黑樹這種數據結構。

Linux內核中的紅黑樹定義在內核文件如下的位置:

如果找不到,可以?find / -name rbtree.h?搜索一下即可,有興趣可以打開文件看下具體實現。

紅黑樹 ?VS ?平衡二叉樹(AVL樹)

  • 插入和刪除操作,一般認為紅黑樹的刪除和插入會比 AVL 樹更快。因為,紅黑樹不像 AVL 樹那樣嚴格的要求平衡因子小于等于1,這就減少了為了達到平衡而進行的旋轉操作次數,可以說是犧牲嚴格平衡性來換取更快的插入和刪除時間。

  • 紅黑樹不要求有不嚴格的平衡性控制,但是紅黑樹的特點,使得任何不平衡都會在三次旋轉之內解決。而 AVL 樹如果不平衡,并不會控制旋轉操作次數,旋轉直到平衡為止。

  • 查找操作,AVL樹的效率更高。因為 AVL 樹設計比紅黑樹更加平衡,不會出現平衡因子超過 1 的情況,減少了樹的平均搜索長度。

Trie樹(前綴樹或字典樹)

Trie來源于單詞 retrieve(檢索),Trie樹也稱為前綴樹或字典樹。利用字符串前綴來查找指定的字符串,縮短查找時間提高查詢效率,主要用于字符串的快速查找和匹配。

為什么要稱其為字典樹呢?因為Trie樹的功能就像字典一樣,想象一下查英文字典,我們會根據首字母找到對應的頁碼,接著根據第二、第三…個單詞,逐步查找到目標單詞,Trie樹的組織思想和字典組織很像,字典樹由此得名。

定義

Trie的核心思想是空間換時間,有 3 個基本性質:

  1. 根節點不包含字符,除根節點外每一個節點都只包含一個字符。
  2. 從根節點到某一節點,路徑上經過的字符連接起來,為該節點對應的字符串。
  3. 每個節點的所有子節點包含的字符都不相同。

比如對單詞序列lru, lua, mem, mcu?建立Trie樹如下:

Trie樹建立和查詢是可以同步進行的,可以在還沒建立出完成的 Trie 樹之前就找到目標數據,而如果用 Hash 表等結構存儲是需要先建立完成表才能開始查詢,這也是 Trie 樹查詢速度快的原因。

應用

Trie樹還用于搜索引擎的關鍵詞提示功能。比如當你在搜索框中輸入檢索單詞的開頭幾個字,搜索引擎就可以自動聯想匹配到可能的詞組出來,這正是Trie樹的最直接應用。

這種結構在海量數據查詢上很有優勢,因為不必為了找到目標數據遍歷整個數據集合,只需按前綴遍歷匹配的路徑即可找到目標數據。

因此,Trie樹還可用于解決類似以下的面試題:

?

給你100000個長度不超過10的單詞。對于每一個單詞,我們要判斷他出沒出現過,如果出現了,求第一次出現在第幾個位置。

?

?

有一個1G大小的一個文件,里面每一行是一個詞,詞的大小不超過16字節,內存限制大小是1M,求頻數最高的100個詞

?

?

1000萬字符串,其中有些是重復的,需要把重復的全部去掉,保留沒有重復的字符串,請問怎么設計和實現?

?

?

一個文本文件,大約有一萬行,每行一個詞,要求統計出其中最頻繁出現的前10個詞,請給出思想,給出時間復雜度分析。

?

總結一下

樹形數據結構有許多變種,這篇文章我們從樹開始,把幾種常見樹形數據結構學習了一遍,包括二叉樹、二叉查找樹(二叉搜索樹)、AVL樹、紅黑樹、B樹、Trie樹。

文章構思的時候想聊聊數據結構中的樹,沒想到步子跨的有點大,大到不知從何說起,因為數據結構中各種樹的變體非常多,一篇文章實難細數,篇幅有限,也只能說是淺嘗輒止,作為樹形數據結構入門,如果要深入的學習,每個知識點還能寫出一篇文章,比如 B+ 樹原理及其在數據庫索引中的應用、紅黑樹的詳細分析等等,這次檸檬只是粗略帶大家走一遍。

在后端開發中,數據結構與算法是后端程序員的基本素養,除了基礎架構部的后端開發同學,雖然我們平常不會經常造輪子,但是掌握基本的數據結構與算法仍然是非常有必要,面試也對相關能力有要求。

回顧往期文章,數據結構的內容寫的比較少,如果大家有興趣,檸檬會再多寫一些相關主題的文章!

感謝各位的閱讀,文章的目的是分享對知識的理解,技術類文章我都會反復求證以求最大程度保證準確性,若文中出現明顯紕漏也歡迎指出,我們一起在探討中學習。

贊(0) 打賞
關注我們
未經允許不得轉載:体育外围 » 圖解:數據結構中的 6 種樹,你心中有數嗎?
分享到: 更多 (0)

評論 搶沙發

這是一種鼓勵

支付寶掃一掃打賞

微信掃一掃打賞