1. 程式人生 > >Linux核心工程導論——資料結構:樹

Linux核心工程導論——資料結構:樹

樹作為一種很常用的資料結構,主要包括二叉搜尋數(BST)、多路搜尋樹(B-樹)、B樹根據葉子節點樹分為二叉樹和多叉樹。根據左右節點是否高度上對稱,分為平衡樹和非平衡樹,平衡樹的一棵空樹或它的左右兩個子樹的高度差的絕對值不超過1,並且左右兩個子樹都是一棵平衡樹。簡單的說就是左右高度差不多。

樹根據其組織節點的形式和新增刪除的操作方式將樹分成的很多種:大的種類包括:

二叉樹

 二叉樹

 二叉查詢樹

 笛卡爾樹

 T

自平衡二叉查詢樹

 AA

 AVL

 紅黑樹

 伸展樹

Trie

 字首樹

 字尾樹

 基數樹

空間劃分樹

 四叉樹

 八叉樹

 k-d

 vp-

 R

 R*

 R+

 X

 M

 線段樹

 優先R

二叉樹是指每個節點有兩個子節點的樹,自平衡二叉查詢樹是指在經過很多新增刪除操作後樹仍是平衡的,因為其新增刪除操作中本身就考慮了平衡問題。

Trie樹又叫做單詞查詢樹,或字典樹。典型應用是用於統計,排序和儲存大量的字串(但不僅限於字串),所以經常被搜尋引擎系統用於文字詞頻統計。它的優點是:利用字串的公共字首來減少查詢時間,最大限度地減少無謂的字串比較,查詢效率比雜湊樹高。


空間劃分樹

BST(查詢二叉樹)

1.所有非葉子結點至多擁有兩個兒子(LeftRight);

2.所有結點儲存一個關鍵字;

3.非葉子結點的左指標指向小於其關鍵字的子樹,右指標指向大於其關鍵字的子樹;

如:


       這樣定義的二叉查詢樹不一定是平衡的,不平衡的二叉樹在實際使用中作用不是很大。因為經過一系列的新增刪除操作後,如果樹不能保持平衡,很容易變成左重右輕或者右重左輕。這樣的查詢樹效率非常低,逼近於排序陣列的遍歷。

B-樹

定義

1.定義任意非葉子結點最多隻有M個兒子;且M>2

2.根結點的兒子數為[2, M]

3.除根結點以外的非葉子結點的兒子數為[M/2, M]

4.每個結點存放至少M/2-1(取上整)和至多M-1個關鍵字;(至少2個關鍵字)

5.非葉子結點的關鍵字個數=指向兒子的指標個數-1

6.非葉子結點的關鍵字:K[1], K[2], …, K[M-1];且K[i]

7.非葉子結點的指標:P[1], P[2], …, P[M];其中P[1]指向關鍵字小於K[1]的子樹,P[M]指向關鍵字大於K[M-1]的子樹,其它P[i]指向關鍵字屬於(K[i-1], K[i])的子樹;

8.所有葉子結點位於同一層;

如:(M=3


       其中P1、P2、P3都代表指標。

搜尋

B-樹的搜尋,從根結點開始,對結點內的關鍵字(有序)序列進行二分查詢,如果命中則結束,否則進入查詢關鍵字所屬範圍的兒子結點;重複,直到所對應的兒子指標為空,或已經是葉子結點;

B-樹的特性:

1.關鍵字集合分佈在整顆樹中;

2.任何一個關鍵字出現且只出現在一個結點中;

3.搜尋有可能在非葉子結點結束;

4.其搜尋效能等價於在關鍵字全集內做一次二分查詢;

5.自動層次控制;

由於限制了除根結點以外的非葉子結點,至少含有M/2個兒子,確保了結點的至少利用率,其最底搜尋效能為O(LogN)。

應用

       搜尋取值範圍內的數。

B+樹:為磁碟應用優化B-樹設計

B+樹是專門為了在磁碟上儲存而設計出來的資料結構。不考慮實際的磁碟情況,B+樹對於B-樹沒有什麼優勢,甚至有劣勢。但是由於磁碟的特殊性質B+樹在磁碟的檔案系統和資料庫的應用遠比B-樹要合適。

定義

B+樹是B-樹的變體,也是一種多路搜尋樹:

1.其定義基本與B-樹同,除了:

2.非葉子結點的子樹指標與關鍵字個數相同;

3.非葉子結點的子樹指標P[i],指向關鍵字值屬於[ K[i], K[i+1])的子樹(B-樹是開區間);

5.為所有葉子結點增加一個鏈指標;

6.所有關鍵字都在葉子結點出現;

如:(M=3)

 

搜尋

B+的搜尋與B-樹也基本相同,區別是B+樹只有達到葉子結點才命中(B-樹可以在非葉子結點命中),其效能也等價於在關鍵字全集做一次二分查詢;

B+的特性

1.所有關鍵字都出現在葉子結點的連結串列中(稠密索引),且連結串列中的關鍵字恰好是有序的;

2.不可能在非葉子結點命中;

3.非葉子結點相當於是葉子結點的索引(稀疏索引),葉子結點相當於是儲存(關鍵字)資料的資料層;

4.更適合檔案索引系統;

應用

1.    不同於B-樹只適合隨機檢索,B+樹同時支援隨機檢索和順序檢索,在實際中應用比較多。

2.       B+樹比B-樹更適合實際應用中作業系統的檔案索引和資料庫索引。

在實際的涉及到磁碟搜尋中,一般使用B+樹。原因是B+樹與B-樹在資料組織方面的區別。B+樹的所有記錄都放在葉子節點,而B-樹則是分散在整個樹中。看起來從上到下的搜尋B-樹可能不用到葉子節點就可以搜尋到資料,不考慮實際的情況,確實B樹能更快速的查詢。

但實際的情況是磁碟是分塊的,每一塊的大小是有限制的,B-樹每個節點既有資料又有指標,這個資料在應用中可不會只是一個整數,而且大小還經常是不一樣的。這就決定了磁碟的一個塊可以放少數的B-樹節點,但可以放很多B+樹的非葉子節點。由於搜尋是一個涉及幾乎全部節點的操作,B+樹的非葉子節點不包含實際的資料,只作為索引使用,所以相當於通過一個短小的索引表(所佔用物理空間)就可以定位出葉子節點,最終檢查葉子節點只需要一次。而B-樹每次查詢索引都需要檢查一個帶有資料的節點,如此導致其走過的節點所佔用的資料空間大小明顯大。在磁碟中,分散的,多次讀取是非常浪費磁碟效能的,導致B-樹在磁碟中的查詢效率遠遠低於B+樹。有人可以說全部讀取到記憶體,B-樹的效率不就高了嗎?對於資料系統,把節點全部讀取到記憶體再處理是不現實的,因為通常節點成千上網甚至千萬。

B*樹:為磁碟應用優化B+樹設計

B+樹的變體,在B+樹的非根和非葉子結點再增加指向兄弟的指標,提高空間的利用率。其區別是兄弟節點如果一個太滿,就將一部分資料轉移到不那麼滿的兄弟節點。如果兄弟們都滿了,就新建兄弟節點。而B+樹只會在一個兄弟滿的時候新建新的兄弟節點。

B*樹定義了非葉子結點關鍵字個數至少為(2/3)*M,即塊的最低使用率為2/3(代替B+樹的1/2);

B+樹的分裂:當一個結點滿時,分配一個新的結點,並將原結點中1/2的資料複製到新結點,最後在父結點中增加新結點的指標;B+樹的分裂隻影響原結點和父結點,而不會影響兄弟結點,所以它不需要指向兄弟的指標;

B*樹的分裂:當一個結點滿時,如果它的下一個兄弟結點未滿,那麼將一部分資料移到兄弟結點中,再在原結點插入關鍵字,最後修改父結點中兄弟結點的關鍵字(因為兄弟結點的關鍵字範圍改變了);如果兄弟也滿了,則在原結點與兄弟結點之間增加新結點,並各複製1/3的資料到新結點,最後在父結點增加新結點的指標;

所以,B*樹分配新結點的概率比B+樹要低,空間使用率更高;

小結

BST樹:二叉搜尋樹,每個結點只儲存一個關鍵字,等於則命中,小於走左結點,大於走右結點;

B-樹(B樹):多路搜尋樹,每個結點儲存M/2到M個關鍵字,非葉子結點儲存指向關鍵字範圍的子結點;所有關鍵字在整顆樹中出現,且只出現一次,非葉子結點可以命中;

B+樹:在B-樹基礎上,為葉子結點增加連結串列指標,所有關鍵字都在葉子結點中出現,非葉子結點作為葉子結點的索引;B+樹總是到葉子結點才命中;

B*樹:在B+樹基礎上,為非葉子結點也增加連結串列指標,將結點的最低利用率從1/2提高到2/3;

所以在linux應用中,自然

Linux核心B+樹

       Linux核心中實現了一個通用的B+樹,這個樹非常簡單,以至於不適合實際的磁碟應用。以至於很多檔案系統都自己又實現了一份B類樹。

標頭檔案位於 include/linux,定義檔案位於 lib目錄下。可以看出這是linux提供的工具程式碼。

Linux核心Radix樹(基樹)

Linux基數樹(radix tree)是將指標與long整數鍵值相關聯的機制,它儲存有效率,並且可快速查詢,用於指標與整數值的對映(如:IDR機制)、記憶體管理等。需要特別注意的是,linux的基樹與課本里所學的資料結構的基樹是完全不同的。這個資料結構設計是用來取代稀疏整數陣列的。

radix樹是通用的字典型別資料結構,radix樹又稱為PAT位樹(Patricia Trie or crit bit tree)。Linux核心使用了資料型別unsigned long的固定長度輸入的版本。每級代表了輸入空間固定位數。

radix tree是一種多叉搜尋樹,樹的葉子結點是實際的資料條目。每個結點有一個固定的、2^n指標指向子結點(每個指標稱為槽slot),並有一個指標指向父結點。

Linux核心利用radix樹在檔案內偏移快速定位檔案快取頁,圖4是一個radix樹樣例,該radix樹的分叉為4(22),樹高為4,樹的每個葉子結點用來快速定位8位檔案內偏移,可以定位4x4x4x4=256頁,如:圖中虛線對應的兩個葉子結點的路徑組成值0x00000010和0x11111010,指向檔案內相應偏移所對應的快取頁。


Linux radix樹每個結點有64個slot,與資料型別long的位數相同,圖1顯示了一個有3級結點的radix樹,每個資料條目(item)可用3個6位的鍵值(key)進行索引,鍵值從左到右分別代表第1~3層結點位置。沒有孩子的結點在圖中不出現。因此,radix樹為稀疏樹提供了有效的儲存,代替固定尺寸陣列提供了鍵值到指標的快速查詢。

 

圖1 一個3級結點的radix樹及其鍵值表示

Linux基樹與trie樹非常類似,只是一個是用來查詢定位字串,每一個是用來查詢定位整數。

Linux核心紅黑樹

       紅黑樹是就是平衡二叉查詢樹的實現。單純的查詢二叉樹由於多次新增刪除可能不平衡,導致效率變低,所以核心中實際使用的是紅黑樹。

       紅黑樹通過定義一部分節點是紅的,一部分是黑的,在插入刪除時根據預先制定的紅黑規則對樹做一些變換,以保證每次新增刪除操作後樹都是保持平衡的。也就是說紅與黑都是為了達成保持平衡的演算法所設計的附屬的手段。


       從圖中也可以看出,紅黑樹也是一顆查詢樹。都是左子節點<本節點<右子節點。

總結

       從以上可以看出,樹在實際應用的主要作用就是快速的比較並定位值。針對不同的場景和不同的應用有不同的變體。


優先排序列表

紅黑樹

區間樹

根樹


訊號量和自旋鎖

這些待續哈偷笑

IDR

IDR機制在Linux核心中指的是整數ID管理機制。實質上來講,這就是一種將一個整數ID號和一個指標關聯在一起的機制。這個機制最早在03年2月加入核心,當時作為POSIX定時器的一個補丁。現在,核心中很多地方都可以找到它的身影。

IDR機制適用在那些需要把某個整數和特定指標關聯在一起的地方。例如,在IIC匯流排中,每個裝置都有自己的地址,要想在總線上找到特定的裝置,就必須要先發送裝置的地址。當介面卡要訪問總線上的IIC裝置時,首先要知道它們的ID號,同時要在核心中建立一個用於描述該裝置的結構體,和驅動程式。將ID號和裝置結構體結合起來,如果使用陣列進行索引,一旦ID號很大,則用陣列索引會佔據大量記憶體空間。這顯然不可能。或者用連結串列,但是,如果匯流排中實際存在的裝置很多,則連結串列的查詢效率會很低。此時,IDR機制應運而生。該機制內部採用radix tree實現,可以很方便的將整數和指標關聯起來,並且具有很高的搜尋效率。

相關推薦

Linux核心工程導論——資料結構

樹 樹作為一種很常用的資料結構,主要包括二叉搜尋數(BST)、多路搜尋樹(B-樹)、B樹根據葉子節點樹分為二叉樹和多叉樹。根據左右節點是否高度上對稱,分為平衡樹和非平衡樹,平衡樹的一棵空樹或它的左右兩個子樹的高度差的絕對值不超過1,並且左右兩個子樹都是一棵平衡樹。簡單的說

Linux核心工程導論——資料結構連結串列與雜湊

         scatterlist table由於可以被拼接(chain),不同的scatterlist如果所指向的記憶體是相鄰的還可以被合併,所以其遍歷格外複雜。1.4 llistllist全稱是Lock-less NULL terminated single linked list,意思是不需要加鎖

Linux核心工程導論——程序ELF檔案執行原理(2)

ELF強符號與弱符號(本小節是轉別人的)我們經常在程式設計中碰到一種情況叫符號重複定義。多個目標檔案中含有相同名字全域性符號的定義,那麼這些目標檔案連結的時候將會出現符號重複定義的錯誤。比如我們在目標檔案A和目標檔案B都定義了一個全域性整形變數global,並將它們都初始化,

Linux核心工程導論——網路Netfilter概覽

簡介最早的核心包過濾機制是ipfwadm,後來是ipchains,再後來就是iptables/netfilter了。再往後,也就是現在是nftables。不過nftables與iptables還處於爭雄階段,誰能勝出目前還沒有定論。但是他們都屬於netfilter專案的子成員

linux核心工程導論-網路tcp擁塞控制

這篇文章本來是在tcp那篇裡面的,但是那篇太長了,不專一。就完善了一下提取出來了。 TCP擁塞控制        擁塞控制討論的是很多個同時存在的tcp連線應該怎麼規劃自己的資料包傳送和接收速度,以在彼此之間共享頻寬,同時與其他實體的機器公平的競爭頻寬,

資料結構&堆的概念持續編輯中

樹---|---:由 一個根結點 和 N個子結點 及 連線線 構成,任意結點間不構成迴路        |---二叉樹---|---:樹的一種,且任意結點最多隻能有兩個子結點        |     &n

Codeforces Round #248 (Div. 2) B題 【資料結構狀陣列】

題目大意:給n(1 ≤ n ≤ 105)個數據(1 ≤ vi ≤ 109),其中有m(1 ≤ m ≤ 105)個問題,分兩種,第一種:給出l,r,讓你求出v[l],v[r]之間的所有資料和;第二種:

重溫資料結構 及 Java 實現

讀完本文你將瞭解到: 資料結構,指的是資料的儲存形式,常見的有線性結構(陣列、連結串列,佇列、棧),還有非線性結構(樹、圖等)。 今天我們來學習下資料結構中的 樹。 什麼是樹 線性結構中,一個節點至多隻有一個頭節點,至多隻有一個尾節點,彼此連線

資料結構

定義與術語 這沒什麼好說的,照搬書上的吧。 一棵樹 T 是由一個或一個以上結點組成的有限集,其中有一個特定的結點 R 稱為 T 的根結點。如果集合 (T -{R}) 非空,那麼集合中的這些結點被劃分為 n 個不相交的子集 T0, T1, ……, Tn

資料結構的遍歷!按先序遍歷建立一棵,分別以先序、中序、後序遍歷輸出

題目:樹的遍歷!按先序遍歷建立一棵樹,分別以先序、中序、後序遍歷輸出 樣例輸入 A B # D # # C E # # F # # 樣例輸出 PreOrder: A B D C E F InOrder: B D A E C F PostOrder: D B E F C A

Linux核心工程導論——前言

想要研究linux核心,使用linux核心,首先要知道linux核心能做到什麼,提供了什麼。我看過很多初學者一進入公司就開始使用linux核心開發核心模組,使用的無論是通訊方式、記憶體介面還是裝置介面仍都是核心早已淘汰掉的。原因是他們通常是直接在網路上搜索來如何完成工作的。

Linux 核心裡的資料結構——位陣列(bitmap)

Linux 核心裡的資料結構——位陣列 Linux 核心中的位陣列和位操作 除了不同的基於鏈式和樹的資料結構以外,Linux 核心也為位陣列(或稱為點陣圖(bitmap))提供了 API。位陣列在 Linux 核心裡被廣泛使用,並且在以下的原始碼檔案中包

linux核心工程導論-Linux使用者和許可權系統

Linux使用者和許可權系統簡介我們使用linux系統,一定會使用一個使用者,沒有使用者就不可能使用任何的系統功能,包括系統呼叫。因為系統呼叫本身也是要有使用者的。我們剛登入一個系統,需要一個login程式,驗證了使用者名稱密碼之後,就會返回給你一個shell。後面執行的內容

OJ 資料結構——建樹

根據題目要求不同,建一顆樹有以下幾種方式 按給定序列依次插入節點建樹 適用BST 按非葉節點的孩子節點資訊建樹 適用一般樹 按中序遍歷序列+其他一種遍歷序列建樹 適用於二叉樹 給定序列插入建數 適用BST 實現 /

資料結構、圖的遍歷

樹的遍歷 先根遍歷:樹非空,先訪問根節點,在按照從左到右的順序遍歷根節點的每一顆子樹。這個訪問順序與這棵樹對應的二叉樹的先序遍歷順序相同。 後根遍歷:樹非空,則按照從左到右的順序遍歷根節點的每一顆子樹,之後在訪問根節點。其訪問順序和這棵樹對應的二叉樹的中序遍歷順序相同。

資料結構

樹(二叉樹(建立,列印,刪除))定義:除了根節點之外,每個結點都有一個父節點,除了葉子節點外所有的節點都有一個或者多個子節點。二叉樹:每個節點最多有兩個葉子節點遍歷:按照某個順序訪問樹中的所有節點。 三種常見的遍歷:前序遍歷,中序遍歷,後續遍歷(可以用遞迴和迴圈兩種方式實現)

Linux核心工程導論——核心除錯

         核心也是一個程式,一般的,除錯程式常用的方法有3種:列印資訊、斷點執行和插入探測點。 列印資訊 printk          最常用的是printk,可以修改核心程式碼,在任何想要列印的地方列印資訊。 健壯性是printk最容易被接受的一個特質,幾乎在任

Linux核心工程導論——linux學習和職業曲線(初學者,中級,高階都可參考)

Linux世界介紹 給自己定級 門外漢: 不會安裝作業系統 不會用虛擬機器(安裝和使用) 入門級: 熟悉常見的發行版,甚至裝過並且能用一些特殊發行版(例如kali)做過一些簡單的圖形介面的使用。 會一些最基礎的命令(例如cd、ps、top、ls、

linux核心分析--核心中的資料結構之紅黑(續)

#include<linux/rbtree.h> #include <linux/string.h> #include "kn_common.h" MODULE_LICENSE("Dual BSD/GPL"); struct student { int id;

linux核心分析--核心中的資料結構之紅黑(四)

紅黑樹由於節點顏色的特性,保證其是一種自平衡的二叉搜尋樹。 紅黑樹的一系列規則雖然實現起來比較複雜,但是遵循起來卻比較簡單,而且紅黑樹的插入,刪除效能也還不錯。 所以紅黑樹在核心中的應用非常廣泛,掌握好紅黑樹,即有利於閱讀核心原始碼,也可以在自己的程式碼中借鑑這種資料結構。 紅黑樹必