動態

詳情 返回 返回

數據結構之美-深入理解樹形結構 - 動態 詳情

一 認識樹形結構

樹形結構是一種廣泛應用的非線性數據結構,它在計算機科學和日常生活中都有廣泛的應用。比如文件系統,郵件系統,編譯器語法樹,決策樹,網絡通信,甚至機器學習當中,都有樹形數據結構的影子。本文旨在梳理日常用到的各類樹形結構以及其優點和劣勢,讓瀆者對樹形結構有一個深入的認知和了解。下面列舉幾類常見的樹形結構的應用場景。

1.1 文件系統

計算機中用於存儲和管理文件的一種系統,它使用樹形結構來組織文件和目錄。每個文件和目錄都存儲在樹的一個節點上,父節點表示上級目錄,子節點表示下級目錄或文件。

img

1.2 域名解析

互聯網的域名解析系統(DNS)採用了層次式樹形結構,這種結構由多個層次組成,最上層為全球根服務器。每個國家、地區以及較大的單位都有自己的域名服務器。這些服務器通過複雜的樹形結構相互連接,形成一個分佈式的數據庫網絡。

我們觀察一個域名,以在線打字練習網站,《巧手打字通》為例,其域名為:www.laidazi.com,就會發現它被兩個點號分割成了三個部分。其中com為頂級域名,laidazi為二級域名,www為三級域名。

這種層次式樹形結構的採用,不僅提高了域名解析的效率,還保證了域名解析的穩定性。即使某個域名服務器出現故障,整個DNS系統的正常運行也不會受到太大影響,因為數據分佈在多個服務器上,可以通過其他可用的服務器完成解析過程。

1.3 郵件系統

郵件通常是通過樹形結構進行組織和管理的。每個郵件可以看作是樹的一個節點,回覆或轉發的郵件可以作為該郵件節點的子節點,形成一個樹形結構。

img

1.4 語法樹

程序代碼在編譯過程中,編譯器使用樹形結構(稱為語法樹)來表示源代碼的結構和語義。每個語法樹節點表示特定的語法結構,如一個函數、一個循環或一個條件語句。

img

1.5 決策樹

在決策樹中,每個節點表示一個決策點,每個分支表示一個可能的決策結果,葉子節點表示決策的最終結果。決策樹常用於分類、預測和優化等領域。

img

二 樹形結構的演進

2.1 線性結構

在工程實踐過程中,數組,鏈表,棧,隊列等數據結構應該都是最常用的,它們都屬於有序的數據元素組合,也被稱為線性結構。它們在數據的存儲,遍歷,排序,分析計算等場景中發揮着重要作用。

img

但是這種線性結構在處理具有層次,分類,關係複雜的數據時,就會顯得力不從心。而樹形結構在這方面卻有着顯著優勢。

img

我們也可以形象的將某些線性結構比類比成由單個子節點串聯起來的特殊樹形結構。

2.2 二叉樹

二叉樹就是每個節點最多存在兩個子節點。其特點是具有有序性,即左子節點總是在右子節點之前。這種特性可以用來解決排序、搜索、數據壓縮等各種問題。二叉樹根據節點的分佈情況,又細分為完全二叉樹和平衡二叉樹。

2.2.1 完全二叉樹

完全二叉樹,由於它的節點是按照層次順序進行排列的,任意節點的左子節點的鍵值小於該節點的鍵值,右子節點的鍵值大於該節點的鍵值,這種基於二分查找的思想,使得查找操作變得相對簡單高效。這種查找方式的時間複雜度為O(log n),在數據量較大時具有較高的效率,因此主要用於優化某些特定的查找操作,日常經常用到的堆排序就是使用了這種完全二叉樹結構。

img

完全二叉樹存在各個鏈路節點的深度參差不齊的問題,極端的情況下,就退變成類似於鏈表數據結構了。這就會使得數據查找的時間複雜度變為O(n)了。

2.2.2 平衡二叉樹

平衡二叉樹是一種自平衡的二叉查找樹,主要彌補了完全二叉樹存在的節點深度差異問題。這在需要頻繁進行查找和插入操作的場景中,能夠在最壞情況下保持O(log n)的查找性能,紅黑樹就是一種自平衡的二叉查找樹,其中每個節點都有一個顏色屬性(紅或黑)。通過顏色和旋轉規則來維護樹的平衡,確保從根到葉子的最長路徑不會超過最短路徑的兩倍。

img

平衡二叉樹為了保證任何時刻樹的高度都保持平衡,需要進行大量的左旋、右旋、高度計算等操作,這會導致插入和刪除節點的速度相對較慢,因此不太適合用於大數據量的場景。

2.3 多路搜索樹

在現實應用場景中,由於二叉樹的子節點個數受到限制,這會導致樹的高度相對較高, 而且附帶的業務數據本身也是存儲到樹的中間節點上的,導致對數據的讀寫要經歷較長的節點路徑。大數據場景下,不可能將所有數據都加載到內存中,因此每個樹節點的訪問都需要磁盤IO, 導致磁盤IO讀寫次數也較高,而磁盤IO的讀寫速度相對內存訪問速度會慢很多,因此二叉樹不適宜處理海量大規模數據。

2.3.1 B-Tree

B-Tree通過增加子節點的個數,減少了樹的層級,可以極大的減少磁盤的IO次數,解決了平衡二叉樹磁盤訪問效率低的問題,B樹本身也是一種自平衡樹,可以自適應地動態更新數據,通過節點裂變的方式保持自平衡。

img

由於B-Tree中間節點(即非葉子節點)上同時存儲了關鍵字,數據記錄和子節點的索引指針數據,體積相對較大,如果都調入到內存中,這會使得內存利用率不高。在查詢效率層面,由於採用中序遍歷策略,每次查詢會隨着靠近根節點的數據而變化,不是很穩定,對於範圍查詢效率也不高。

2.3.2 B+Tree

B+樹(B+Tree)是對B樹的一種改進,它與B樹的不同點主要包括:

•B+樹的所有數據記錄都存儲在葉子節點上,中間節點只存儲關鍵字和子節點索引指針數據;

•B+樹的所有葉子節點被一個鏈表按照數據的從小到大的順序串聯起來。因此B+樹上對數據的結果集的訪問可以不用回溯到非葉子節點,直接通過葉子節點鏈表就可以直接收集,效率比較高。

由於數據結構調整,B+Tree在磁盤讀寫效率、範圍查詢效率和內存利用率等各個方面要優於B-Tree。

img

多路搜索樹對插入、刪除和修改操作都有比較高效的節點調整或裂變等方式來保證樹的平衡性,從而保證O(lgN)的高效查詢時間複雜度。但當應用場景擴展到多維度的空間索引查詢時,由於父子節點兄弟節點之間的大小有序關係表現在多個維度上,節點調整或裂變操作還需要修改數量較多的離得較遠的葉子節點或祖先節點(極限情況可能波及整棵樹),就會導致數據動態更新效率變低。

2.4 多維樹

2.4.1 KD-Tree

KD-Tree(K-Dimensional Tree)是一種高維索引樹形數據結構,用於在k維空間中存儲和檢索實例點。

img

上圖是對6個二維平面座標軸的數據點集合:(2,3), (5,4),(9,6),(4,7),(8,1),(7,2)的存儲。

1.先按照X維度值7把空間一分為二得到x<=7 和 x>7兩部分左子樹和右子樹;

2.再對左子樹按Y維度4再一分為二;

3.按照上面兩個步驟依次遞歸類推,即:第一層先按X維劃分,第二層按Y維度劃分, 第三層按X維度劃分,第4層再按Y維度劃分,循環往復。

KD-Tree適宜處理多維數據,查詢效率較高。一個靜態多維數據集合通過KD-Tree後,查詢時間複雜度是O(lgN)。

2.4.2 KD-B-Tree

KD-B-Tree(K-Dimension-Balanced-Tree)顧名思義,結合了KD-Tree和B+Tree。它主要解決了KD-Tree的二叉樹形式樹高較高,對磁盤IO不夠友好的缺點,引入了B+樹的多叉樹形式,不僅降低了樹高,而且全部數據都存儲在葉子節點,增加了磁盤空間存儲利用率。

img

因為KD-Tree和KD-B-Tree的分層劃分是依據維度依次輪替進行的,動態更新後調整某個中間節點時,變更的當前維度的同時,也需要調整其全部子孫節點中的當前維度值,導致對樹節點的訪問和操作增多,操作耗時增大。因此,KD-Tree更適宜處理的是靜態場景的多維海量數據的查詢操作。

2.4.3 BKD-Tree

BKD-Tree(Block-K-Dimension-Tree ),它是一族(Block)KD-Trees。即它其實是一個森林,不再是一顆樹。

img

該數據結構是由杜克大學的幾位教授基於KD-B-Tree設計的,同時也結合了一種被稱為 “logarithmic method" 的方法,使得靜態數據動態化。

1.不同於KD-B-Tree的多叉樹,BKD-Tree是完全二叉樹。雖然二叉樹不如多叉樹的磁盤IO更友好,但是BKD-Tree仍然採用二叉樹的形式主要原因可能在於:

•採用了完全二叉樹的形式,類似於堆或優先級隊列,能直接通過下標訪問父節點或子節點;

•減少了存儲在中間節點中的索引的存儲空間,極簡緊湊的中間索引節點佔用空間更小,甚至中間節點可以一次性全部調入內存存儲,調用內存訪問索引節點效率更高。

1.採用一種被稱為 “logarithmic method" 的方法使得靜態數據動態化, 極大提高了動態數據更小的效率。

•採用一個二叉的KD-B-Tree的森林。新增加的數據存儲在一個初始支持高效查詢和動態更新的小規模數據結構上M,再通過M和多個小的二叉KD-B-Tree,以一定的策略和時機合併成大的二叉KD-B-Tree,以替換原結構的方式更新數據;

•數據刪除可以採取標記刪除的方式實現,從而實現了多維數值數據的高效率動態更新操作;

在Lucene中,BKD-Tree被用來解決大規模數據集的搜索和過濾操作的效率問題。為了支持高效的數值類或者多維度查詢,Lucene引入了Bkd-Tree。

三 樹形結構的應用

3.1 字典樹

Trie樹通過將字符串的字符作為節點進行存儲,其底層使用的就是多叉樹數據結構。經常用於統計和排序大量的字符串(但不僅限於字符串),所以經常被搜索引擎系統用於文本詞頻統計。其主要優點是就是最大限度地減少無謂的字符串比較,查詢效率比哈希表高。

img

Trie樹前綴匹配常用於搜索提示。如當輸入一個關鍵字,可以自動搜索出可能的選擇。當沒有完全匹配的搜索結果,可以返回前綴最相似的可能。

3.2 隨機森林

隨機森林是一種監督式算法,由眾多決策樹組成,通過自助採樣的方法生成眾多並行式的分類器(決策樹),通過“少數服從多數”的原則來確定最終結果 。隨機森林使用多棵樹來避免和防止單純決策樹過擬合的問題。其主要特點有以下兩點:

1.隨機:隨機選擇樣本,隨機選擇特徵;

2.集成學習:投票選舉策略;

下面舉個實際的例子,比如我們想根據某個人的年齡(age),性別(gender),學歷(edu),行業(industry),居住地(city)共5個特徵屬性來預測其收入層次(高,中,低)。

隨機森林中,每顆樹都可以看做是一顆分類迴歸樹(CART),假設森林中有5可CART樹,總特徵樹N=5,取M=1(表示每個CART樹對應一個不同的特徵)。

img

基於不同的特徵,每一個決策樹都會給出一個收入層次的概率(特徵概率值就是我們模型訓練後的結果),拿圖中的示例來看,預測結果如下:

1.age(20-40):7%,23%,70%;

2.gender(male):3%,27%,70%;

3.edu(大學):6%,14%,80%;

4.city(北京):15,%,20%,65%;

5.industry(互聯網):5%,30%,65%;

綜合取平均結果為:高收入概率-7.2%,中等收入概率-22.8%,低收入概率-70%。

隨機森林採用集成學習方法,具有模型預測準確性高,抗干擾能力,可解釋性強等特點,在風控,語音識別,圖像分類等各種分類場景發揮着很大作用。

3.3 數據庫索引

數據庫索引通常用於加速對數據的訪問,按照數據分佈特點會有不同的數據結構,常見的有B-Tree,B+Tree,R-Tree,Hash Index,Bitmap Index,Bloom Filter等。下面就以mysql的Innodb引擎中使用的B+Tree索引結構為例,探究數據結構如何指導着我們上層應用的。

先看一張典型的B+Tree索引數據結構圖:

img

3.3.1 索引樹深度

索引樹的深度應該越少越好,它會影響磁盤空間的佔用以及索引的查詢效率。影響索引深度的因素主要有以下幾點:

1.索引數據的重複度,重複度越高,索引樹深度相對就會增加;

2.索引字段的數據越小越好,這也會影響索引深度;

3.索引數據的量級,數據量越大,索引樹深度也會增加;

InnoDB數據的最小的存儲單位是Page(默認大小為16k),假設主鍵佔8字節,指針佔6個字節,一行數據記錄大小為1k,那麼,單個非葉子節點可以存儲數據個數:16384/14=1170,單個葉子節點可容納記錄數16K/1K=16條。

數據總容量 = 根節點節點指針數量 * 每頁保存的數據量。

假設一個高度為3的B+樹,它可以存放的記錄數:1170*1170*16=21902400條。

由此得出結論建議,如下:

1.重複度較高的字段(比如性別,狀態等字段)上不適合建立獨立索引,這往往會導致樹的深度增加。如果確實有需要,可以考慮聯合其它重複度低的屬性字段,一起組合建立複合索引;

2.對於索引字段數據過大的問題,可以考慮使用前綴索引來減少索引的大小;

3.確保主鍵索引是唯一的,並且不包含NULL值。這有助於提高查詢速度和數據的完整性;

4.數據記錄長度越大,同等樹深的數據容量就會越少;

5.數據量大了,可以考慮通過分表來緩解樹深度過大的問題;

3.3.2 數據存儲格式

在B+Tree中,主要的存儲結構信息,如下:

1.中間節點存儲鍵值信息、子節點指針、引用計數;

2.葉子節點會存儲鍵值信息、數據記錄指針、刪除標記;

3.葉子節點之間還通過指針相互連接,形成一個鏈表結構,便於順序訪問;

中間節點和葉子節點在InnoDB索引中扮演着不同的角色。中間節點用於組織和維護索引的結構,而葉子節點則存儲了所有的數據記錄,並提供對數據的直接訪問。通過合理地設計和使用索引,可以提高數據庫查詢的性能和效率。

由此得出結論建議,如下:

1.使用覆蓋索引:如果一個索引包含了查詢所需要的所有數據,那麼查詢可以直接通過索引來獲取數據,而不需要回表。這稱為覆蓋索引,可以顯著提高查詢效率;

2.避免使用函數或表達式:數據庫查詢優化器通常基於列的值來選擇和使用索引。當列上的函數或表達式被應用時,優化器可能無法識別或利用這些索引,從而導致索引失效;

3.範圍查找很高效:由於B+Tree葉子節點之間順序鏈接的特性,使得範圍查找非常高效。它能夠快速定位到指定範圍的起始和結束位置,然後通過線性遍歷來獲取所有範圍內的值,從而避免了全樹遍歷。

4.最左前綴原則:當我們創建一個聯合索引時,例如(a,b,c),實際上是創建了一個由列a、b和c組成的B+樹。在這個樹中,每個節點都包含了a、b和c的值,並且按照a、b和c的順序進行排序。如果我們要查找列a和b的值,而跳過了列a直接查找列b的值,那麼B+樹的範圍查找就無法使用了。因為B+樹的範圍查找是基於列的順序進行的,如果跳過了最左邊的列,那麼就無法確定要查找的範圍了。

5.控表的索引個數:過多的索引無論是在存儲空間的佔用方面,還是在索引維護的性能開銷方面(鎖競爭),都有不小的負面影響。索引的設計儘可能的考慮複用;

四 總結

從線性結構到二叉樹,是樹形結構的一次飛躍。二叉樹使得數據結構可以呈現層級關係,大大簡化了數據組織的複雜性。平衡樹在此基礎上,通過平衡操作保持樹的深度在可接受範圍內,從而在插入、刪除等操作時能保持較好的性能。

多路搜索樹如B樹、B+樹等,進一步擴展了樹形結構的應用範圍。它們允許節點有多個子節點,從而在保持樹的平衡性方面更加靈活。多維樹則將樹形結構從一維擴展到多維,使得數據結構能夠更好地適應複雜數據類型和多維數據關係。

在應用方面,樹形結構被廣泛應用人工智能、圖形學、網絡協議等眾多領域。掌握其底層實現原理後,能夠更好的解決工程測的痛點問題。比如:在數據庫系統中,索引常常採用樹形結構來提高查詢效率,瞭解其內部實現後,就能夠在資源使用率和性能方面擁有更好的表現。

Add a new 評論

Some HTML is okay.