檸檬哥整理了50本計算機相關的電子書,關注公衆號「後端技術學堂」,回覆「1024」我發給你,回覆「進羣」拉你進百人讀者技術交流羣。程序員
數據結構這門課程是計算機相關專業的基礎課,數據結構指的是數據在計算機中的存儲、組織方式。面試
咱們在學習數據結構時候,會遇到各類各樣的基礎數據結構,好比堆棧、隊列、數組、鏈表、樹...這些基本的數據結構類型有各自的特色,不一樣數據結構適用於解決不一樣場景下的問題。算法
樹形結構相比數組、鏈表、堆棧這些數據結構來講,稍微複雜一點點,但樹形結構能夠用於解決不少實際問題,由於現實世界事物之間的關係每每不是線性關聯的,而「樹」剛好適合描述這種非線性關係。數據庫
今天就帶你們一塊兒學習下,數據結構中的各類「樹」,這也是面試中常常考察的內容,手撕二叉樹是常規套路,對候選人也頗有區分度,學完這篇文章,相信你們都會心中有「樹」了。後端
什麼是樹?現實中的樹你們都見過,在數據結構中也有樹,此樹非彼樹,不過數據結構的樹和現實中的樹在形態上確實有點相像。數組
樹是非線性的數據結構,用來模擬具備樹狀結構性質的數據集合,它是由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 這個名字的由來,是它的兩個發明者G. M. Adelson-Velsky 和 Evgenii Landis 的縮寫,AVL最初是他們兩人在1962 年的論文「An algorithm for the organization of information」中提出來一種數據結構。
AVL 樹是一種平衡二叉查找樹,二叉查找樹咱們已經知道,那平衡是什麼意思呢?
咱們舉個天平的例子,天平兩端的重量要差很少才能平衡,不然就會出現向一邊傾斜的狀況。把這個概念遷移到二叉樹中來,根節點看做是天平的中點,左子樹的高度放在天平左邊,右子樹的高度放在天平右邊,當左右子樹的高度相差「不是特別多」,稱爲是平衡的二叉樹。
AVL樹有更嚴格的定義:在二叉查找樹中,任一節點對應的兩棵子樹的最大高度差爲 1,這樣的二叉查找樹稱爲平衡二叉樹。其中左右子樹的高度差也有個專業的叫法:平衡因子。
一旦因爲插入或刪除致使左右子樹的高度差大於1,此時就須要旋轉某些節點調整樹高度,使其再次達到平衡狀態,這個過程稱爲旋轉再平衡。
保持樹平衡的目的是能夠控制查找、插入和刪除在平均和最壞狀況下的時間複雜度都是O(log n),相比普通二叉樹最壞狀況的時間複雜度是 O(n) ,AVL樹把最壞狀況的複雜度控制在可接受範圍,很是合適對算法執行時間敏感類的應用。
B樹是魯道夫·拜爾(Rudolf Bayer)1972年在波音研究實驗室(Boeing Research Labs)工做時發明的,關於B樹名字的由來至今是個未解之謎,有人猜是Bayer的首字母,也有人說是波音實驗室(Boeing Research Labs)的Boeing首字母縮寫,雖然B樹這個名字來源撲朔迷離,咱們內心也沒點 B 樹,但不影響今天咱們來學習它。
一個 m 階的B樹是一個有如下屬性的樹
若是以前不瞭解,相信第一眼看完定義確定是蒙圈,不過多看幾遍好好理解一下就行了,畫個圖例,對照着看看:
B樹是全部節點的平衡因子均等於0的多路查找樹(AVL樹是平衡因子不大於 1 的二路查找樹)。
B 樹節點能夠保存多個數據,使得 B 樹能夠不用像 AVL 樹那樣爲了保持平衡頻繁的旋轉節點。
B樹的多路的特性,下降了樹的高度,因此B樹相比於平衡二叉樹顯得矮胖不少。
B樹很是適合保存在磁盤中的數據讀取,由於每次讀取都會有一次磁盤IO,高度下降減小了磁盤IO的次數。
B樹經常使用於實現數據庫索引,典型的實現,MongoDB索引用B樹實現,MySQL的Innodb 存儲引擎用B+樹存放索引。
說到B樹不得不提起它的好兄弟B+樹,不過這裏不展開細說,只需知道,B+樹是對B樹的改進,數據都放在葉子節點,非葉子節點只存數據索引。
紅黑樹也是一種特殊的「二叉查找樹」。
到目前爲止咱們學習的 AVL 樹和即將學習的紅黑樹都是二叉查找樹的變體,可見二叉查找樹真的是很是重要基礎二叉樹,若是忘了它的定義能夠先回頭看看。
紅黑樹中每一個結點都被標記了紅黑屬性,紅黑樹除了有普通的「二叉查找樹」特性以外,還有如下的特徵:
這些性質有興趣能夠自行研究,不過,如今你只須要知道,這些約束確保了紅黑樹的關鍵特性:從根到葉子的最長的可能路徑很少於最短的可能路徑的兩倍長。
而節點的路徑長度決定着對節點的查詢效率,這樣咱們確保了,最壞狀況下的查找、插入、刪除操做的時間複雜度不超過O(log n),而且有較高的插入和刪除效率。
紅黑樹在實際應用中比較普遍,有不少已經落地的實踐,好比學習C++的同窗都知道會接觸到 STL 標準庫,而STL容器中的map、set、multiset、multimap 底層實現都是基於紅黑樹。
再好比,Linux內核中也有紅黑樹的實現,Linux系統在實現EXT3文件系統、虛擬內存管理系統,都有使用到紅黑樹這種數據結構。
Linux內核中的紅黑樹定義在內核文件以下的位置:
若是找不到,能夠 find / -name rbtree.h
搜索一下便可,有興趣能夠打開文件看下具體實現。
插入和刪除操做,通常認爲紅黑樹的刪除和插入會比 AVL 樹更快。由於,紅黑樹不像 AVL 樹那樣嚴格的要求平衡因子小於等於1,這就減小了爲了達到平衡而進行的旋轉操做次數,能夠說是犧牲嚴格平衡性來換取更快的插入和刪除時間。
紅黑樹不要求有不嚴格的平衡性控制,可是紅黑樹的特色,使得任何不平衡都會在三次旋轉以內解決。而 AVL 樹若是不平衡,並不會控制旋轉操做次數,旋轉直到平衡爲止。
查找操做,AVL樹的效率更高。由於 AVL 樹設計比紅黑樹更加平衡,不會出現平衡因子超過 1 的狀況,減小了樹的平均搜索長度。
Trie來源於單詞 retrieve(檢索),Trie樹也稱爲前綴樹或字典樹。利用字符串前綴來查找指定的字符串,縮短查找時間提升查詢效率,主要用於字符串的快速查找和匹配。
爲何要稱其爲字典樹呢?由於Trie樹的功能就像字典同樣,想象一下查英文字典,咱們會根據首字母找到對應的頁碼,接着根據第2、第三...個單詞,逐步查找到目標單詞,Trie樹的組織思想和字典組織很像,字典樹由此得名。
Trie的核心思想是空間換時間,有 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+ 樹原理及其在數據庫索引中的應用、紅黑樹的詳細分析等等,此次檸檬只是粗略帶你們走一遍。
在後端開發中,數據結構與算法是後端程序員的基本素養,除了基礎架構部的後端開發同窗,雖然咱們日常不會常常造輪子,可是掌握基本的數據結構與算法仍然是很是有必要,面試也對相關能力有要求。回顧往期文章,數據結構的內容寫的比較少,若是你們有興趣,檸檬會再多寫一些相關主題的文章!
感謝各位的閱讀,文章的目的是分享對知識的理解,技術類文章我都會反覆求證以求最大程度保證準確性,若文中出現明顯紕漏也歡迎指出,咱們一塊兒在探討中學習。
Hi,我是檸檬,熱愛技術,也愛生活!一枚一線互聯網大廠後端程序員,我的技術公衆號主要分享後端開發相關的技術文章,每篇文章都是我精心創做,若是文章對你有幫助,請幫點亮「在看」也能夠「分享」給須要的小夥伴,你的分享和在看對檸檬很重要,在此先行謝過各位了!我是lemon,咱們下期再見!
若是文章對你有幫助,答應我不要白piao好嗎? 「點贊、評論、轉發」激勵我持續創做。
能夠微信搜索公衆號「 後端技術學堂 」回覆「1024」獲取50本計算機電子書,回覆「進羣」拉你進讀者技術交流羣,文章每週持續更新,咱們下期見!