從B 樹、B+ 樹、B* 樹談到R 樹

做者:July、weedge、Frankie。編程藝術室出品。php

說明:本文從B樹開始談起,而後論述B+樹、B*樹,最後談到R 樹。其中B樹、B+樹及B*樹部分由weedge完成,R 樹部分由Frankie完成,全文最終由July統稿修訂完成。html

出處:http://blog.csdn.net/v_JULY_v java

 

第一節、B樹、B+樹、B*樹

1.前言:node

動態查找樹主要有:二叉查找樹(Binary Search Tree),平衡二叉查找樹(Balanced Binary Search Tree),紅黑樹(Red-Black Tree ),B-tree/B+-tree/ B*-tree (B~Tree)。前三者是典型的二叉查找樹結構,其查找的時間複雜度O(log2N)與樹的深度相關,那麼下降樹的深度天然會提升查找效率。mysql

可是我們有面對這樣一個實際問題:就是大規模數據存儲中,實現索引查詢這樣一個實際背景下,樹節點存儲的元素數量是有限的(若是元素數量很是多的話,查找就退化成節點內部的線性查找了),這樣致使二叉查找樹結構因爲樹的深度過大而形成磁盤I/O讀寫過於頻繁,進而致使查詢效率低下(爲何會出現這種狀況,待會在外部存儲器-磁盤中有所解釋),那麼如何減小樹的深度(固然是不能減小查詢的數據量),一個基本的想法就是:採用多叉樹結構(因爲樹節點元素數量是有限的,天然該節點的子樹數量也就是有限的)。面試

也 就是說,由於磁盤的操做費時費資源,若是過於頻繁的屢次查找勢必效率低下。那麼如何提升效率,即如何避免磁盤過於頻繁的屢次查找呢?根據磁盤查找存取的次 數每每由樹的高度所決定,因此,只要咱們經過某種較好的樹結構減小樹的結構儘可能減小樹的高度,那麼是否是便能有效減小磁盤查找存取的次數呢?那這種有效的 樹結構是一種怎樣的樹呢?算法

這樣咱們就提出了一個新的查找樹結構——多路查找樹。根據平衡二叉樹的啓發,天然就想到平衡多路查找樹結構,也就是這篇文章所要闡述的第一個主題B~tree,即B樹結構(後面,咱們將看到,B樹的各類操做能使B樹保持較低的高度,從而達到有效避免磁盤過於頻繁的查找存取操做,從而有效提升查找效率)。sql

B-tree(B-tree樹即B樹,B即Balanced,平衡的意思)這棵神奇的樹是在Rudolf BayerEdward M. McCreight(1970)寫的一篇論文《Organization and Maintenance of Large Ordered Indices》中首次提出的(wikipedia中:http://en.wikipedia.org/wiki/B-tree,闡述了B-tree名字來源以及相關的開源地址)。數據庫

在開始介紹B~tree以前,先了解下相關的硬件知識,才能很好的瞭解爲何須要B~tree這種外存數據結構。 編程

 

2.外存儲器磁盤

計算機存儲設備通常分爲兩種內存儲器(main memory)和外存儲器(external memory) 內存存取速度快,但容量小,價格昂貴,並且不能長期保存數據(在不通電狀況下數據會消失)

外存儲器—磁盤是一種直接存取的存儲設備(DASD)。它是以存取時間變化不大爲特徵的。能夠直接存取任何字符組,且容量大、速度較其它外存設備更快。

2.1磁盤的構造

磁盤是一個扁平的圓盤(與電唱機的唱片相似)。盤面上有許多稱爲磁道的圓圈,數據就記錄在這些磁道上。磁盤能夠是單片的,也能夠是由若干盤片組成的盤組,每一盤片上有兩個面。以下圖11.3中所示的6片盤組爲例,除去最頂端和最底端的外側面不存儲數據以外,一共有10個面能夠用來保存信息。

                           

 

當磁盤驅動器執行讀/寫功能時。盤片裝在一個主軸上,並繞主軸高速旋轉,當磁道在讀/寫頭(又叫磁頭) 下經過時,就能夠進行數據的讀 / 寫了。

通常磁盤分爲固定頭盤(磁頭固定)和活動頭盤。固定頭盤的每個磁道上都有獨立的磁頭,它是固定不動的,專門負責這一磁道上數據的讀/寫。

活動頭盤 (如上圖)的磁頭是可移動的。每個盤面上只有一個磁頭(磁頭是雙向的,所以正反盤面都能讀寫)。它能夠從該面的一個磁道移動到另外一個磁道。全部磁頭都裝在同一個動臂上,所以不一樣盤面上的全部磁頭都是同時移動的(行動整齊劃一)。當盤片繞主軸旋轉的時候,磁頭與旋轉的盤片造成一個圓柱體。各個盤面上半徑相同的磁道組成了一個圓柱面,咱們稱爲柱面 。所以,柱面的個數也就是盤面上的磁道數。 

2.2磁盤的讀/寫原理和效率

磁盤上數據必須用一個三維地址惟一標示:柱面號、盤面號、塊號(磁道上的盤塊)

/寫磁盤上某一指定數據須要下面3個步驟:

(1)  首先移動臂根據柱面號使磁頭移動到所須要的柱面上,這一過程被稱爲定位或查找

(2)  如上圖11.3中所示的6盤組示意圖中,全部磁頭都定位到了10個盤面的10條磁道上(磁頭都是雙向的)。這時根據盤面號來肯定指定盤面上的磁道。

(3) 盤面肯定之後,盤片開始旋轉,將指定塊號的磁道段移動至磁頭下。

通過上面三個步驟,指定數據的存儲位置就被找到。這時就能夠開始讀/寫操做了。

訪問某一具體信息,由3部分時間組成:

查找時間(seek time) Ts: 完成上述步驟(1)所須要的時間。這部分時間代價最高,最大可達到0.1s左右。

等待時間(latency time) Tl: 完成上述步驟(3)所須要的時間。因爲盤片繞主軸旋轉速度很快,通常爲7200/(電腦硬盤的性能指標之一, 家用的普通硬盤的轉速通常有5400rpm(筆記本)7200rpm幾種)所以通常旋轉一圈大約0.0083s

傳輸時間(transmission time) Tt: 數據經過系統總線傳送到內存的時間,通常傳輸一個字節(byte)大概0.02us=2*10^(-8)s

磁盤讀取數據是以盤塊(block)爲基本單位的。位於同一盤塊中的全部數據都能被一次性所有讀取出來。而磁盤IO代價主要花費在查找時間Ts上。所以咱們應該儘可能將相關信息存放在同一盤塊,同一磁道中。或者至少放在同一柱面或相鄰柱面上,以求在讀/寫信息時儘可能減小磁頭來回移動的次數,避免過多的查找時間Ts

因此,在大規模數據存儲方面,大量數據存儲在外存磁盤中,而在外存磁盤中讀取/寫入塊(block)中某數據時,首先須要定位到磁盤中的某塊,如何有效地查找磁盤中的數據,須要一種合理高效的外存數據結構,就是下面所要重點闡述的B-tree結構,以及相關的變種結構:B+-tree結構和B*-tree結構。

3.B- 樹 

     3.1什麼是B-樹

具體講解以前,有一點,再次強調下:B-樹,即爲B樹。由於B樹的原英文名稱爲B-tree,而國內不少人喜歡把B-tree譯做B-樹,其實,這是個很是很差的直譯,很容易讓人產生誤解。如人們可能會覺得B-樹是一種樹,而B樹又是一種一種樹。而事實上是,B-tree就是指的B樹。特此說明。

我 們知道,B  樹是爲了磁盤或其它存儲設備而設計的一種多叉(下面你會看到,相對於二叉,B樹每一個內結點有多個分支,即多叉)平衡查找樹。與本blog以前介紹的紅黑樹 很類似,但在下降磁盤I/0操做方面要更好一些。許多數據庫系統都通常使用B樹或者B樹的各類變形結構,以下文即將要介紹的B+樹,B*樹來存儲信息。

 B 樹與紅黑樹最大的不一樣在於,B樹的結點能夠有許多子女,從幾個到幾千個。那爲何又說B樹與紅黑樹很類似呢?由於與紅黑樹同樣,一棵含n個結點的B樹的高 度也爲O(lgn),但可能比一棵紅黑樹的高度小許多,應爲它的分支因子比較大。因此,B樹能夠在O(logn)時間內,實現各類如插入 (insert),刪除(delete)等動態集合操做。

如 下圖所示,便是一棵B樹,一棵關鍵字爲英語中輔音字母的B樹,如今要從樹種查找字母R(包含n[x]個關鍵字的內結點x,x有n[x]+1]個子女(也就 是說,一個內結點x若含有n[x]個關鍵字,那麼x將含有n[x]+1個子女)。全部的葉結點都處於相同的深度,帶陰影的結點爲查找字母R時要檢查的結 點):

 

相信,從上圖你能輕易的看到,一個內結點x若含有n[x]個關鍵字,那麼x將含有n[x]+1個子女。如含有2個關鍵字D H的內結點有3個子女,而含有3個關鍵字Q T X的內結點有4個子女。

    B樹的定義,從下文中,你將看到,或者是用階,或者是用度,以下段文字所述:
     Unfortunately, the literature on B-trees is not uniform in its use of  terms relating to B-Trees. (Folk & Zoellick 1992, p. 362) Bayer  & McCreight (1972), Comer (1979), and others define the order of  B-tree as the minimum number of keys in a non-root node. Folk &  Zoellick (1992) points out that terminology is ambiguous because the  maximum number of keys is not clear. An order 3 B-tree might hold a  maximum of 6 keys or a maximum of 7 keys. (Knuth 1998,TAOCP p. 483)  avoids the problem by defining the order to be maximum number of  children (which is one more than the maximum number of keys).

    from: http://en.wikipedia.org/wiki/Btree#Technical_description

    用階定義的B樹

    B 樹又叫平衡多路查找樹。一棵m階的B (注:切勿簡單的認爲一棵m階的B樹是m叉樹,雖然存在四叉樹八叉樹KD,及vp/R樹/R*樹/R+樹/X樹/M樹/線段樹/希爾伯特R樹/優先R樹等空間劃分樹,但與B樹徹底不等同)的特性以下

  1. 樹中每一個結點最多含有m個孩子(m>=2);

  2. 除根結點和葉子結點外,其它每一個結點至少有[ceil(m / 2)]個孩子(其中ceil(x)是一個取上限的函數);

  3. 若根結點不是葉子結點,則至少有2個孩子(特殊狀況:沒有孩子的根結點,即根結點爲葉子結點,整棵樹只有一個根節點);

  4. 全部葉子結點都出如今同一層,葉子結點不包含任何關鍵字信息(能夠看作是外部接點或查詢失敗的接點,實際上這些結點不存在,指向這些結點的指針都爲null);(讀者反饋@冷嶽這裏有錯,葉子節點只是沒有孩子和指向孩子的指針,這些節點也存在,也有元素。@研究者July:其實,關鍵是把什麼當作葉子結點,由於如紅黑樹中,每個NULL指針即當作葉子結點,只是沒畫出來而已)。

  5. 每一個非終端結點中包含有n個關鍵字信息: (n,P0,K1,P1,K2,P2,......,Kn,Pn)。其中:
           a)   Ki (i=1...n)爲關鍵字,且關鍵字按順序升序排序K(i-1)< Ki。
           b)   Pi爲指向子樹根的接點,且指針P(i-1)指向子樹種全部結點的關鍵字均小於Ki,但都大於K(i-1)。 
           c)   關鍵字的個數n必須知足: [ceil(m / 2)-1]<= n <= m-1。
    以下圖所示:


    用度定義的B樹

      針對上面的5點,再闡述下:B樹中每個結點能包含的關鍵字(如以前上面的D H和Q T X)數有一個上界和下界。這個下界能夠用一個稱做B樹的最小度數(算法導論中文版上譯做度數,最小度數即內節點中節點最小孩子數目)m(m>=2)表示。

  • 每一個非根的內結點至多有m個子女,每一個非根的結點必須至少含有m-1個關鍵字,若是樹是非空的,則根結點至少包含一個關鍵字;

  • 每一個結點可包含至多2m-1個關鍵字。因此一個內結點至多可有2m個子女。若是一個結點剛好有2m-1個關鍵字,咱們就說這個結點是滿的(而稍後介紹的B*樹做爲B樹的一種經常使用變形,B*樹中要求每一個內結點至少爲2/3滿,而不是像這裏的B樹所要求的至少半滿);

  • 當關鍵字數m=2(t=2的意思是,mmin=2,m能夠>=2)時的B樹是最簡單的有不少人會所以誤認爲B樹就是二叉查找樹,但二叉查找樹就是二叉查找樹,B樹就是B樹,B樹是一棵含有m(m>=2)個關鍵字的平衡多路查找樹,此時,每一個內結點可能所以而含有2個、3個或4個子女,亦即一棵2-3-4樹,然而在實際中,一般採用大得多的t值。

     B樹中的每一個結點根據實際狀況能夠包含大量的關鍵字信息和分支(固然是不能超過磁盤塊的大小,根據磁盤驅動(disk  drives)的不一樣,通常塊的大小在1k~4k左右);這樣樹的深度下降了,這就意味着查找一個元素只要不多結點從外存磁盤中讀入內存,很快訪問到要查 找的數據。若是你看完上面關於B樹定義的介紹,思惟感受不夠清晰,請繼續參閱下文第6小節、B樹的插入、刪除操做 部分

    3.2B樹的類型和節點定義

    B樹的類型和節點定義以下圖所示:

 

 

    3.3文件查找的具體過程(涉及磁盤IO操做)

爲了簡單,這裏用少許數據構造一棵3叉樹的形式,實際應用中的B樹結點中關鍵字不少的。上面的圖中好比根結點,其中17表示一個磁盤文件的文件名;小紅方塊表示這個17文件內容在硬盤中的存儲位置;p1表示指向17左子樹的指針。

其結構能夠簡單定義爲:

typedef struct {

    /*文件數*/

    int  file_num;

    /*文件名(key)*/

    char * file_name[max_file_num];

    /*指向子節點的指針*/

     BTNode * BTptr[max_file_num+1];

     /*文件在硬盤中的存儲位置*/

     FILE_HARD_ADDR offset[max_file_num];

}BTNode;

假如每一個盤塊能夠正好存放一個B樹的結點(正好存放2個文件名)。那麼一個BTNODE結點就表明一個盤塊,而子樹指針就是存放另一個盤塊的地址。

下面,我們來模擬下查找文件29的過程:

  1. 根據根結點指針找到文件目錄的根磁盤塊1,將其中的信息導入內存。【磁盤IO操做 1次】    

  2. 此時內存中有兩個文件名1七、35和三個存儲其餘磁盤頁面地址的數據。根據算法咱們發現:17<29<35,所以咱們找到指針p2

  3. 根據p2指針,咱們定位到磁盤塊3,並將其中的信息導入內存。【磁盤IO操做 2次】    

  4. 此時內存中有兩個文件名26,30和三個存儲其餘磁盤頁面地址的數據。根據算法咱們發現:26<29<30,所以咱們找到指針p2

  5. 根據p2指針,咱們定位到磁盤塊8,並將其中的信息導入內存。【磁盤IO操做 3次】    

  6. 此時內存中有兩個文件名28,29。根據算法咱們查找到文件名29,並定位了該文件內存的磁盤地址。

分析上面的過程,發現須要3次磁盤IO操做和3次內存查找操做。關於內存中的文件名查找,因爲是一個有序表結構,能夠利用折半查找提升效率。至於IO操做是影響整個B樹查找效率的決定因素。

固然,若是咱們使用平衡二叉樹的磁盤存儲結構來進行查找,磁盤4次,最多5次,並且文件越多,B樹比平衡二叉樹所用的磁盤IO操做次數將越少,效率也越高

3.4B樹的高度

    根據上面的例子咱們能夠看出,對於輔存作IO讀的次數取決於B樹的高度。而B樹的高度由什麼決定的呢?

    若B樹某一非葉子節點包含N個關鍵字,則此非葉子節點含有N+1個孩子結點,而全部的葉子結點都在第I層,咱們能夠得出:

  1. 由於根至少有兩個孩子,所以第2層至少有兩個結點。

  2. 除根和葉子外,其它結點至少有┌m/2┐個孩子,

  3. 所以在第3層至少有2*┌m/2┐個結點,

  4. 在第4層至少有2*(┌m/2┐^2)個結點,

  5. 在第 I 層至少有2*(┌m/2┐^(l-2) )個結點,因而有: N+1 ≥ 2*┌m/2┐I-2;

  6. 考慮第L層的結點個數爲N+1,那麼2*(┌m/2┐^(l-2))≤N+1,也就是L層的最少結點數恰好達到N+1個,即: I≤ log┌m/2┐((N+1)/2 )+2;

  因此

  • 當B樹包含N個關鍵字時,B樹的最大高度爲l-1(由於計算B樹高度時,葉結點所在層不計算在內),即:l - 1 = log┌m/2┐((N+1)/2 )+1

  這個B樹的高度公式從側面顯示了B樹的查找效率是至關高的曾在一次面試中被問到,一棵含有N個總關鍵字數的m階的B樹的最大高度是多少?答曰:log_ceil(m/2)(N+1)/2 + 1 (上面中關於m階B樹的第1點特性已經提到:樹中每一個結點含有最多含有m個孩子,即m知足:ceil(m/2)<=m<=m。而樹中每一個結點含孩子數越少,樹的高度則越大,故如此)。在2012微軟4月份的筆試中也問到了此問題。
此外,還有讀者反饋,說上面的B樹的高度計算公式與算法導論一書上的不一樣,然後我特地翻看了算法導論第18章關於B樹的高度一節的內容,以下圖所示:在 上圖中書上所舉的例子中,也許,根據咱們大多數人的理解,它的高度應該是4,而書上卻說的是「一棵高度爲3的B樹」。我想,此時,你也就明白了,算法導論 一書上的高度的定義是從「0」開始計數的,而咱們中國人的習慣是樹的高度是從「1」開始計數的。特此說明。July、二零一二年九月二十七日。

4.B+-tree

B+-tree:是應文件系統所需而產生的一種B-tree的變形樹。

一棵m階的B+樹和m階的B樹的異同點在於:

      1.n棵子樹的結點中含有n-1 個關鍵字; (與B 樹n棵子樹有n-1個關鍵字 保持一致,參照:http://en.wikipedia.org/wiki/B%2B_tree#Overview,而下面B+樹的圖可能有問題,請讀者注意)

      2.全部的葉子結點中包含了所有關鍵字的信息,及指向含有這些關鍵字記錄的指針,且葉子結點自己依關鍵字的大小自小而大的順序連接。 (而B 樹的葉子節點並無包括所有須要查找的信息)

      3.全部的非終端結點能夠當作是索引部分,結點中僅含有其子樹根結點中最大(或最小)關鍵字。 (而B 樹的非終節點也包含須要查找的有效信息)

 

 

a)     爲何說B+-treeB 樹更適合實際應用中操做系統的文件索引和數據庫索引?

1) B+-tree的磁盤讀寫代價更低

B+-tree的內部結點並無指向關鍵字具體信息的指針。所以其內部結點相對B 樹更小。若是把全部同一內部結點的關鍵字存放在同一盤塊中,那麼盤塊所能容納的關鍵字數量也越多。一次性讀入內存中的須要查找的關鍵字也就越多。相對來講IO讀寫次數也就下降了。

    舉個例子,假設磁盤中的一個盤塊容納16bytes,而一個關鍵字2bytes,一個關鍵字具體信息指針2bytes。一棵9B-tree(一個結點最多8個關鍵字)的內部結點須要2個盤快。而B+ 樹內部結點只須要1個盤快。當須要把內部結點讀入內存中的時候,B 樹就比B+ 樹多一次盤塊查找時間(在磁盤中就是盤片旋轉的時間)

2) B+-tree的查詢效率更加穩定

因爲非終結點並非最終指向文件內容的結點,而只是葉子結點中關鍵字的索引。因此任何關鍵字的查找必須走一條從根結點到葉子結點的路。全部關鍵字查詢的路徑長度相同,致使每個數據的查詢效率至關。

讀者點評
本 文評論下第149樓,fanyy1991針對上文所說的兩點,道:我的以爲這兩個緣由都不是主要緣由。數據庫索引採用B+樹的主要緣由是  B樹在提升了磁盤IO性能的同時並無解決元素遍歷的效率低下的問題。正是爲了解決這個問題,B+樹應運而生。B+樹只要遍歷葉子節點就能夠實現整棵樹的 遍歷。並且在數據庫中基於範圍的查詢是很是頻繁的,而B樹不支持這樣的操做(或者說效率過低)。

b)    B+-tree的應用: VSAM(虛擬存儲存取法)文件(來源論文 the ubiquitous Btree 做者:D COMER - 1979 )

 

 

5.B*-tree

B*-treeB+-tree的變體,在B+樹的基礎上(全部的葉子結點中包含了所有關鍵字的信息,及指向含有這些關鍵字記錄的指針),B*樹中非根和非葉子結點再增長指向兄弟的指針;B*樹定義了非葉子結點關鍵字個數至少爲(2/3)*M,即塊的最低使用率爲2/3(代替B+樹的1/2)。給出了一個簡單實例,以下圖所示:

 

B+樹的分裂:當一個結點滿時,分配一個新的結點,並將原結點中1/2的數據複製到新結點,最後在父結點中增長新結點的指針;B+樹的分裂隻影響原結點和父結點,而不會影響兄弟結點,因此它不須要指向兄弟的指針。

B*樹的分裂:當一個結點滿時,若是它的下一個兄弟結點未滿,那麼將一部分數據移到兄弟結點中,再在原結點插入關鍵字,最後修改父結點中兄弟結點的關鍵字(由於兄弟結點的關鍵字範圍改變了);若是兄弟也滿了,則在原結點與兄弟結點之間增長新結點,並各複製1/3的數據到新結點,最後在父結點增長新結點的指針。

因此,B*樹分配新結點的機率比B+樹要低,空間使用率更高;

 

六、B樹的插入、刪除操做

上面3小節簡單介紹了利用B樹這種結構如何訪問外存磁盤中的數據的狀況,下面我們經過另一個實例來對這棵B樹的插入(insert,刪除(delete)基本操做進行詳細的介紹。但在此以前,我們還得簡單回顧下一棵m階的B 的特性,以下:

  1. 樹中每一個結點含有最多含有m個孩子,即m知足:ceil(m/2)<=m<=m。

  2. 除根結點和葉子結點外,其它每一個結點至少有[ceil(m / 2)]個孩子(其中ceil(x)是一個取上限的函數);

  3. 若根結點不是葉子結點,則至少有2個孩子(特殊狀況:沒有孩子的根結點,即根結點爲葉子結點,整棵樹只有一個根節點);

  4. 全部葉子結點都出如今同一層,葉子結點不包含任何關鍵字信息(能夠看作是外部接點或查詢失敗的接點,實際上這些結點不存在,指向這些結點的指針都爲null);

  5. 每一個非終端結點中包含有n個關鍵字信息: (n,P0,K1,P1,K2,P2,......,Kn,Pn)。其中:
           a)   Ki (i=1...n)爲關鍵字,且關鍵字按順序升序排序K(i-1)< Ki。
           b)   Pi爲指向子樹根的接點,且指針P(i-1)指向子樹種全部結點的關鍵字均小於Ki,但都大於K(i-1)。 
           c)   除根結點以外的結點的關鍵字的個數n必須知足: [ceil(m / 2)-1]<= n <= m-1(葉子結點也必須知足此條關於關鍵字數的性質,根結點除外)。

ok,下面我們以一棵5階(即樹中任一結點至多含有4個關鍵字,5棵子樹B樹實例進行講解(以下圖所示)

備註:關鍵字數(2-4個)針對--非根結點(包括葉子結點在內),孩子數(3-5個)--針對根結點和葉子結點以外的內結點。固然,根結點是必須至少有2個孩子的,否則就成直線型搜索樹了。下圖中,讀者能夠看到關鍵字數2-4個,內結點孩子數3-5個:

關鍵字爲大寫字母,順序爲字母升序。

結點定義以下:

typedef struct{

   int Count;         // 當前節點中關鍵元素數目

   ItemType Key[4];   // 存儲關鍵字元素的數組

   long Branch[5];    // 僞指針數組,(記錄數目)方便判斷合併和分裂的狀況

} NodeType;

 

6.一、插入(insert)操做

插入一個元素時,首先在B樹中是否存在,若是不存在,即在葉子結點處結束,而後在葉子結點中插入該新的元素,注意:若是葉子結點空間足夠,這裏須要向右移動該葉子結點中大於新插入關鍵字的元素,若是空間滿了以至沒有足夠的空間去添加新的元素,則將該結點進 行「分裂」,將一半數量的關鍵字元素分裂到新的其相鄰右結點中,中間關鍵字元素上移到父結點中(固然,若是父結點空間滿了,也一樣須要「分裂」操做),而 且當結點中關鍵元素向右移動了,相關的指針也須要向右移。若是在根結點插入新元素,空間滿了,則進行分裂操做,這樣原來的根結點中的中間關鍵字元素向上移 動到新的根結點中,所以致使樹的高度增長一層。以下圖所示:


一、OK,下面我們經過一個實例來逐步講解下。插入如下字符字母到一棵空的樹中(非根結點關鍵字數小了(小於2個)就合併,大了(超過4個)就分裂):C N G A H E K Q M F W L T Z D P R X Y S首先,結點空間足夠,4個字母插入相同的結點中,以下圖:

 

二、當我們試着插入H時,結點發現空間不夠,以至將其分裂成2個結點,移動中間元素G上移到新的根結點中,在實現過程當中,我們把AC留在當前結點中,而HN放置新的其右鄰居結點中。以下圖:

 

三、當我們插入E,K,Q時,不須要任何分裂操做

 

 

 

 

 

 

 

 

 

 

 

 

四、插入M須要一次分裂,注意M剛好是中間關鍵字元素,以至向上移到父節點中

 

五、插入F,W,L,T不須要任何分裂操做

 

六、插入Z時,最右的葉子結點空間滿了,須要進行分裂操做,中間元素T上移到父節點中,注意經過上移中間元素,樹最終仍是保持平衡,分裂結果的結點存在2個關鍵字元素。

 

七、插入D時,致使最左邊的葉子結點被分裂,D剛好也是中間元素,上移到父節點中,而後字母P,R,X,Y陸續插入不須要任何分裂操做(別忘了,樹中至多5個孩子)。

 

八、最後,當插入S時,含有N,P,Q,R的結點須要分裂,把中間元素Q上移到父節點中,可是狀況來了,父節點中空間已經滿了,因此也要進行分裂,將父節點中的中間元素M上移到新造成的根結點中,注意之前在父節點中的第三個指針在修改後包括DG節點中。這樣具體插入操做的完成,下面介紹刪除操做,刪除操做相對於插入操做要考慮的狀況多點。

 

6.二、刪除(delete)操做

首先查找B樹中需刪除的元素,若是該元素在B樹中存在,則將該元素在其結點中進行刪除,若是刪除該元素後,首先判斷該元素是否有左右孩子結點,若是有,則上移孩子結點中的某相近元素(「左孩子最右邊的節點」「右孩子最左邊的節點」)到父節點中,而後移動以後狀況;若是沒有,直接刪除後,移動以後的狀況

刪除元素,移動相應元素以後,若是某結點中元素數目(即關鍵字數)小於ceil(m/2)-1,則須要看其某相鄰兄弟結點是否豐滿(結點中元素個數大於ceil(m/2)-1)(還 記得第一節中關於B樹的第5個特性中的c點麼?: c)除根結點以外的結點(包括葉子結點)的關鍵字的個數n必須知足: (ceil(m /  2)-1)<= n <=  m-1。m表示最多含有m個孩子,n表示關鍵字數。在本小節中舉的一顆B樹的示例中,關鍵字數n知足:2<=n<=4),若是豐滿,則向父節點借一個元素來知足條件;若是其相鄰兄弟都剛脫貧,即借了以後其結點數目小於ceil(m/2)-1,則該結點與其相鄰的某一兄弟結點進行合併」成一個結點,以此來知足條件。那我們經過下面實例來詳細瞭解吧。

以上述插入操做構造的一棵5階B樹(樹中最多含有m(m=5)個孩子,所以關鍵字數最小爲ceil(m / 2)-1=2。仍是這句話,關鍵字數小了(小於2個)就合併,大了(超過4個)就分裂)爲例,依次刪除H,T,R,E

一、首先刪除元素H,固然首先查找HH在一個葉子結點中,且該葉子結點元素數目3大於最小元素數目ceil(m/2)-1=2,則操做很簡單,我們只須要移動K至原來H的位置,移動LK的位置(也就是結點中刪除元素後面的元素向前移動)

 

二、下一步,刪除T,由於T沒有在葉子結點中,而是在中間結點中找到,我們發現他的繼承者W(字母升序的下個元素),將W上移到T的位置,而後將原包含W的孩子結點中的W進行刪除,這裏剛好刪除W後,該孩子結點中元素個數大於2,無需進行合併操做。

 

三、下一步刪除RR在葉子結點中,可是該結點中元素數目爲2,刪除致使只有1個元素,已經小於最小元素數目ceil(5/2)-1=2,而由前面咱們已經知道:若是其某個相鄰兄弟結點中比較豐滿(元素個數大於ceil(5/2)-1=2),則能夠向父結點借一個元素,而後將最豐滿的相鄰兄弟結點中上移最後或最前一個元素到父節點中(有沒有看到紅黑樹中左旋操做的影子?),在這個實例中,右相鄰兄弟結點中比較豐滿(3個元素大於2),因此先向父節點借一個元素W下移到該葉子結點中,代替原來S的位置,S前移;而後X在相鄰右兄弟結點中上移到父結點中,最後在相鄰右兄弟結點中刪除X,後面元素前移。

 

四、最後一步刪除E, 刪除後會致使不少問題,由於E所在的結點數目恰好達標,恰好知足最小元素個數(ceil(5/2)-1=2,而相鄰的兄弟結點也是一樣的狀況,刪除一個元素都不能知足條件,因此須要該節點與某相鄰兄弟結點進行合併操做;首先移動父結點中的元素(該元素在兩個須要合併的兩個結點元素之間)下移到其子結點中,而後將這兩個結點進行合併成一個結點。因此在該實例中,我們首先將父節點中的元素D下移到已經刪除E而只有F的結點中,而後將含有DF的結點和含有A,C的相鄰兄弟結點進行合併成一個結點。

 

五、也許你認爲這樣刪除操做已經結束了,其實否則,在看看上圖,對於這種特殊狀況,你當即會發現父節點只包含一個元素G,沒達標(由於非根節點包括葉子結點的關鍵字數n必須知足於2=<n<=4,而此處的n=1),這是不可以接受的。若是這個問題結點的相鄰兄弟比較豐滿,則能夠向父結點借一個元素。假設這時右兄弟結點(含有Q,X)有一個以上的元素(Q右邊還有元素),而後我們將M下移到元素不多的子結點中,將Q上移到M的位置,這時,Q的左子樹將變成M的右子樹,也就是含有NP結點被依附在M的右指針上。因此在這個實例中,我們沒有辦法去借一個元素,只能與兄弟結點進行合併成一個結點,而根結點中的惟一元素M下移到子結點,這樣,樹的高度減小一層。

 

爲了進一步詳細討論刪除的狀況,再舉另一個實例

這裏是一棵不一樣的5B樹,那我們試着刪除C

 

因而將刪除元素C的右子結點中的D元素上移到C的位置,可是出現上移元素後,只有一個元素的結點的狀況。

又由於含有E的結點,其相鄰兄弟結點纔剛脫貧(最少元素個數爲2),不可能向父節點借元素,因此只能進行合併操做,因而這裏將含有A,B的左兄弟結點和含有E的結點進行合併成一個結點。

 

這樣又出現只含有一個元素F結點的狀況,這時,其相鄰的兄弟結點是豐滿的(元素個數爲3>最小元素個數2,這樣就能夠想父結點借元素了,把父結點中的J下移到該結點中,相應的若是結點中J後有元素則前移,而後相鄰兄弟結點中的第一個元素(或者最後一個元素)上移到父節點中,後面的元素(或者前面的元素)前移(或者後移);注意含有KL的結點之前依附在M的左邊,如今變爲依附在J的右邊。這樣每一個結點都知足B樹結構性質。

 

從以上操做可看出:除根結點以外的結點(包括葉子結點)的關鍵字的個數n知足:(ceil(m / 2)-1)<= n <= m-1,即2<=n<=4。這也佐證了我們以前的觀點。刪除操做完。

 

7.總結

經過以上介紹,大體將B樹,B+樹,B*樹總結以下:

B樹:有序數組+平衡多叉樹;

B+樹:有序數組鏈表+平衡多叉樹;

B*樹:一棵豐滿的B+樹。

    在大規模數據存儲的文件系統中,B~tree系列數據結構,起着很重要的做用,對於存儲不一樣的數據,節點相關的信息也是有所不一樣,這裏根據本身的理解,畫的一個查找以職工號爲關鍵字,職工號爲38的記錄的簡單示意圖。(這裏假設每一個物理塊容納3個索引,磁盤的I/O操做的基本單位是塊(block),磁盤訪問很費時,採用B+樹有效的減小了訪問磁盤的次數。)

對於像MySQLDB2Oracle等數據庫中的索引結構得有較深刻的瞭解才行,建議去找一些B 樹相關的開源代碼研究。

走進搜索引擎的做者梁斌老師針對B樹、B+樹給出了他的意見(爲了真實性,特引用其原話,未做任何改動): 「B+樹還有一個最大的好處,方便掃庫,B樹必須用中序遍歷的方法按序掃庫,而B+樹直接從葉子結點挨個掃一遍就完了,B+樹支持range-query很是方便,而B樹不支持。這是數據庫選用B+樹的最主要緣由。

    好比要查 5-10之間的,B+樹一把到5這個標記,再一把到10,而後串起來就好了,B樹就很是麻煩。B樹的好處,就是成功查詢特別有利,由於樹的高度整體要比B+樹矮。不成功的狀況下,B樹也比B+樹稍稍佔一點點便宜。

    B樹好比你的例子中查,17的話,一把就獲得結果了,
有不少基於頻率的搜索是選用B樹,越頻繁query的結點越往根上走,前提是須要對query作統計,並且要對key作一些變化。

    另外B樹也好B+樹也好,根或者上面幾層由於被反覆query,因此這幾塊基本都在內存中,不會出現讀磁盤IO,通常已啓動的時候,就會主動換入內存。」很是感謝。

    Bucket Li:"mysql 底層存儲是用B+樹實現的,知道爲何麼。內存中B+樹是沒有優點的,可是一到磁盤,B+樹的威力就出來了"。

 


第二節、R樹:處理空間存儲問題

相信通過上面第一節的介紹,你已經對B樹或者B+樹有所瞭解。這種樹能夠很是好的處理一維空間存儲的問題。B樹是一棵平衡樹,它是把一維直線分爲若干段線段,當咱們查找知足某個要求的點的時候,只要去查找它所屬的線段便可。依我看來,這種思想其實就是先找一個大的空間,再逐步縮小所要查找的空間,最終在一個本身設定的最小不可分空間內找出知足要求的解。一個典型的B樹查找以下:

 

要查找某一知足條件的點,先去找到知足條件的線段,而後遍歷所在線段上的點,便可找到答案。

B樹是一種相對來講比較複雜的數據結構,尤爲是在它的刪除與插入操做過程當中,由於它涉及到了葉子結點的分解與合併。因爲本文第一節已經詳細介紹了B樹和B+樹,下面直接開始介紹咱們的第二個主角:R樹。

 

簡介

1984年,加州大學伯克利分校的Guttman發表了一篇題爲「R-trees: a dynamic index structure for spatial searching」的論文,向世人介紹了R樹這種處理高維空間存儲問題的數據結構。本文即是基於這篇論文寫做完成的,所以若是你們對R樹很是有興趣,我想最好仍是參考一下原著:)。爲表示對這位牛人的尊重,給個引用先:

Guttman, A.; 「R-trees: a dynamic index structure for spatial searching,」 ACM, 1984, 14

R樹在數據庫等領域作出的功績是很是顯著的。它很好的解決了在高維空間搜索等問題。舉個R樹在現實領域中可以解決的例子:查找20英里之內全部的餐廳。若是沒有R樹你會怎麼解決?通常狀況下咱們會把餐廳的座標(x,y)分爲兩個字段存放在數據庫中,一個字段記錄經度,另外一個字段記錄緯度。這樣的話咱們就須要遍歷全部的餐廳獲取其位置信息,而後計算是否知足要求。若是一個地區有100家餐廳的話,咱們就要進行100次位置計算操做了,若是應用到谷歌地圖這種超大數據庫中,這種方法便一定不可行了。

R樹就很好的解決了這種高維空間搜索問題。它把B樹的思想很好的擴展到了多維空間,採用了B樹分割空間的思想,並在添加、刪除操做時採用合併、分解結點的方法,保證樹的平衡性。所以,R樹就是一棵用來存儲高維數據的平衡樹。

OK,接下來,本文將詳細介紹R樹的數據結構以及R樹的操做。至於R樹的擴展與R樹的性能問題,能夠查閱相關論文。

 

R樹的數據結構

如上所述,R樹是B樹在高維空間的擴展,是一棵平衡樹。每一個R樹的葉子結點包含了多個指向不一樣數據的指針,這些數據能夠是存放在硬盤中的,也能夠是存在內存中。根據R樹的這種數據結構,當咱們須要進行一個高維空間查詢時,咱們只須要遍歷少數幾個葉子結點所包含的指針,查看這些指針指向的數據是否知足要求便可。這種方式使咱們沒必要遍歷全部數據便可得到答案,效率顯著提升。下圖1是R樹的一個簡單實例:

咱們在上面說過,R樹運用了空間分割的理念,這種理念是如何實現的呢?R樹採用了一種稱爲MBR(Minimal Bounding Rectangle)的方法,在此我把它譯做「最小邊界矩形」。從葉子結點開始用矩形(rectangle)將空間框起來,結點越往上,框住的空間就越大,以此對空間進行分割。有點不懂?不要緊,繼續往下看。在這裏我還想提一下,R樹中的R應該表明的是Rectangle(此處參考wikipedia上關於R樹的介紹),而不是大多數國內教材中所說的Region(不少書把R樹稱爲區域樹,這是有誤的)。咱們就拿二維空間來舉例。下圖是Guttman論文中的一幅圖:

 

我來詳細解釋一下這張圖。先來看圖(b

  1. 首先咱們假設全部數據都是二維空間下的點,圖中僅僅標誌了R8區域中的數據,也就是那個shape of data object。別把那一塊不規則圖形當作一個數據,咱們把它看做是多個數據圍成的一個區域。爲了實現R樹結構,咱們用一個最小邊界矩形剛好框住這個不規則區域,這樣,咱們就構造出了一個區域:R8R8的特色很明顯,就是正正好好框住全部在此區域中的數據。其餘實線包圍住的區域,如R9R10R12等都是一樣的道理。這樣一來,咱們一共獲得了12個最最基本的最小矩形。這些矩形都將被存儲在子結點中。

  2. 下一步操做就是進行高一層次的處理。咱們發現R8R9R10三個矩形距離最爲靠近,所以就能夠用一個更大的矩形R3剛好框住這3個矩形。

  3. 一樣道理,R15R16R6剛好框住,R11R12R4剛好框住,等等。全部最基本的最小邊界矩形被框入更大的矩形中以後,再次迭代,用更大的框去框住這些矩形。

我想你們都應該理解這個數據結構的特徵了。用地圖的例子來解釋,就是全部的數據都是餐廳所對應的地點,先把相鄰的餐廳劃分到同一塊區域,劃分好全部餐廳以後,再把鄰近的區域劃分到更大的區域,劃分完畢後再次進行更高層次的劃分,直到劃分到只剩下兩個最大的區域爲止。要查找的時候就方便了。

下面就能夠把這些大大小小的矩形存入咱們的R樹中去了。根結點存放的是兩個最大的矩形,這兩個最大的矩形框住了全部的剩餘的矩形,固然也就框住了全部的數據。下一層的結點存放了次大的矩形,這些矩形縮小了範圍。每一個葉子結點都是存放的最小的矩形,這些矩形中可能包含有n個數據。

在這裏,讀者先不要去糾結於如何劃分數據到最小區域矩形,也不要糾結怎樣用更大的矩形框住小矩形,這些都是下一節咱們要討論的。

講完了基本的數據結構,咱們來說個實例,如何查詢特定的數據。又以餐廳爲例,假設我要查詢廣州市天河區天河城附近一千米的全部餐廳地址怎麼辦?

  1. 打開地圖(也就是整個R樹),先選擇國內仍是國外(也就是根結點)。

  2. 而後選擇華南地區(對應第一層結點),選擇廣州市(對應第二層結點),

  3. 再選擇天河區(對應第三層結點),

  4. 最後選擇天河城所在的那個區域(對應葉子結點,存放有最小矩形),遍歷全部在此區域內的結點,看是否知足咱們的要求便可。

怎麼樣,其實R樹的查找規則跟查地圖很像吧?對應下圖:

一棵R樹知足以下的性質:

1.     除非它是根結點以外,全部葉子結點包含有mM個記錄索引(條目)。做爲根結點的葉子結點所具備的記錄個數能夠少於m。一般,m=M/2

2.     對於全部在葉子中存儲的記錄(條目),I是最小的能夠在空間中徹底覆蓋這些記錄所表明的點的矩形(注意:此處所說的「矩形」是能夠擴展到高維空間的)。

3.     每個非葉子結點擁有mM個孩子結點,除非它是根結點。

4.     對於在非葉子結點上的每個條目,i是最小的能夠在空間上徹底覆蓋這些條目所表明的店的矩形(同性質2)。

5.     全部葉子結點都位於同一層,所以R樹爲平衡樹。

葉子結點的結構

先來探究一下葉子結點的結構。葉子結點所保存的數據形式爲:(I, tuple-identifier)

      其中,tuple-identifier表示的是一個存放於數據庫中的tuple,也就是一條記錄,它是n維的。I是一個n維空間的矩形,並能夠剛好框住這個葉子結點中全部記錄表明的n維空間中的點。I=(I0,I1,…,In-1)。其結構以下圖所示:

下圖描述的就是在二維空間中的葉子結點所要存儲的信息。

 

在這張圖中,I所表明的就是圖中的矩形,其範圍是a<=I0<=bc<=I1<=d。有兩個tuple-identifier,在圖中即表示爲那兩個點。這種形式徹底能夠推廣到高維空間。你們簡單想一想三維空間中的樣子就能夠了。這樣,葉子結點的結構就介紹完了。

 

非葉子結點

      非葉子結點的結構其實與葉子結點很是相似。想象一下B樹就知道了,B樹的葉子結點存放的是真實存在的數據,而非葉子結點存放的是這些數據的「邊界」,或者說也算是一種索引(有疑問的讀者能夠回顧一下上述第一節中講解B樹的部分

      一樣道理,R樹的非葉子結點存放的數據結構爲:(I, child-pointer)

      其中,child-pointer是指向孩子結點的指針,I是覆蓋全部孩子結點對應矩形的矩形。這邊有點拗口,但我想不是很難懂?給張圖:

 

D,E,F,G爲孩子結點所對應的矩形。A爲可以覆蓋這些矩形的更大的矩形。這個A就 是這個非葉子結點所對應的矩形。這時候你應該悟到了吧?不管是葉子結點仍是非葉子結點,它們都對應着一個矩形。樹形結構上層的結點所對應的矩形可以徹底覆 蓋它的孩子結點所對應的矩形。根結點也惟一對應一個矩形,而這個矩形是能夠覆蓋全部咱們擁有的數據信息在空間中表明的點的。

我我的感受這張圖畫的不那麼精確,應該是矩形A要剛好覆蓋D,E,F,G,而不該該再留出這麼多沒用的空間了。但爲尊重原圖的繪製者,特不做修改。

 

R樹的操做

這一部分也許是編程者最關注的問題了。這麼高效的數據結構該如何去實現呢?這即是這一節須要闡述的問題。

 

搜索

R樹的搜索操做很簡單,跟B樹上的搜索十分類似。它返回的結果是全部符合查找信息的記錄條目。而輸入是什麼?就我我的的理解,輸入不只僅是一個範圍了,它更能夠當作是一個空間中的矩形。也就是說,咱們輸入的是一個搜索矩形。

先給出僞代碼:

FunctionSearch

描述:假設T爲一棵R樹的根結點,查找全部搜索矩形S覆蓋的記錄條目。

S1:[查找子樹] 若是T是非葉子結點,若是T所對應的矩形與S有重合,那麼檢查全部T中存儲的條目,對於全部這些條目,使用Search操做做用在每個條目所指向的子樹的根結點上(即T結點的孩子結點)。

S2:[查找葉子結點] 若是T是葉子結點,若是T所對應的矩形與S有重合,那麼直接檢查S所指向的全部記錄條目。返回符合條件的記錄。

咱們經過下圖來理解這個Search操做。

 

 

陰影部分所對應的矩形爲搜索矩形。它與根結點對應的最大的矩形(未畫出)有重疊。這樣將Search操做做用在其兩個子樹上。兩個子樹對應的矩形分別爲R1R2。搜索R1,發現與R1中的R4矩形有重疊,繼續搜索R4。最終在R4所包含的R11R12兩個矩形中查找是否有符合條件的記錄。搜索R2的過程一樣如此。很顯然,該算法進行的是一個迭代操做。

 

插入

      R樹的插入操做也同B樹的插入操做相似。當新的數據記錄須要被添加入葉子結點時,若葉子結點溢出,那麼咱們須要對葉子結點進行分裂操做。顯然,葉子結點的插入操做會比搜索操做要複雜。插入操做須要一些輔助方法纔可以完成。

來看一下僞代碼:

FunctionInsert

描述:將新的記錄條目E插入給定的R樹中。

I1[爲新記錄找到合適插入的葉子結點] 開始ChooseLeaf方法選擇葉子結點L以放置記錄E

I2[添加新記錄至葉子結點] 若是L有足夠的空間來放置新的記錄條目,則向L中添加E。若是沒有足夠的空間,則進行SplitNode方法以得到兩個結點LLL,這兩個結點包含了全部原來葉子結點L中的條目與新條目E

I3[將變換向上傳遞] 開始對結點L進行AdjustTree操做,若是進行了分裂操做,那麼同時須要對LL進行AdjustTree操做。

I4[對樹進行增高操做] 若是結點分裂,且該分裂向上傳播致使了根結點的分裂,那麼須要建立一個新的根結點,而且讓它的兩個孩子結點分別爲原來那個根結點分裂後的兩個結點。

 

FunctionChooseLeaf

描述:選擇葉子結點以放置新條目E

CL1[Initialize] 設置N爲根結點。

CL2[葉子結點的檢查] 若是N爲葉子結點,則直接返回N

CL3[選擇子樹] 若是N不是葉子結點,則遍歷N中的結點,找出添加E.I時擴張最小的結點,並把該結點定義爲F。若是有多個這樣的結點,那麼選擇面積最小的結點。

CL4[降低至葉子結點] N設爲F,從CL2開始重複操做。

 

FunctionAdjustTree

描述:葉子結點的改變向上傳遞至根結點以改變各個矩陣。在傳遞變換的過程當中可能會產生結點的分裂。

AT1[初始化] N設爲L

AT2[檢驗是否完成] 若是N爲根結點,則中止操做。

AT3[調整父結點條目的最小邊界矩形] PN的父節點,EN爲指向在父節點P中指向N的條目。調整EN.I以保證全部在N中的矩形都被剛好包圍。

AT4[向上傳遞結點分裂] 若是N有一個剛剛被分裂產生的結點NN,則建立一個指向NN的條目ENN。若是P有空間來存放ENN,則將ENN添加到P中。若是沒有,則對P進行SplitNode操做以獲得PPP

AT5[升高至下一級] 若是N等於L且發生了分裂,則把NN置爲PP。從AT2開始重複操做。

 

一樣,咱們用圖來更加直觀的理解這個插入操做。

 

 

    咱們來經過圖分析一下插入操做。如今咱們須要插入R21這個矩形。開始時咱們進行ChooseLeaf操做。在根結點中有兩個條目,分別爲R1R2。其實R1已經徹底覆蓋了R21,而若向R2中添加R21,則會使R2.I增大不少。顯然咱們選擇R1插入。而後進行下一級的操做。相比於R4,向R3中添加R21會更合適,由於R3覆蓋R21所需增大的面積相對較小。這樣就在B8B9B10所在的葉子結點中插入R21。因爲葉子結點沒有足夠空間,則要進行分裂操做。

    插入操做以下圖所示:

 

這個插入操做其實相似於第一節中B樹的插入操做,這裏再也不具體介紹,不過想必看過上面的僞代碼你們應該也清楚了。

 

刪除

R樹的刪除操做與B樹的刪除操做會有所不一樣,不過同B樹同樣,會涉及到壓縮等操做。相信讀者看完如下的僞代碼以後會有所體會。R樹的刪除一樣是比較複雜的,須要用到一些輔助函數來完成整個操做。

僞代碼以下:

FunctionDelete

描述:將一條記錄E從指定的R樹中刪除。

D1[找到含有記錄的葉子結點] 使用FindLeaf方法找到包含有記錄E的葉子結點L。若是搜索失敗,則直接終止。

D2[刪除記錄] EL中刪除。

D3[傳遞記錄] L使用CondenseTree操做

D4[縮減樹] 當通過以上調整後,若是根結點只包含有一個孩子結點,則將這個惟一的孩子結點設爲根結點。

 

FunctionFindLeaf

描述:根結點爲T,指望找到包含有記錄E的葉子結點。

FL1[搜索子樹] 若是T不是葉子結點,則檢查每一條T中的條目F,找出與E所對應的矩形相重合的F(沒必要徹底覆蓋)。對於全部知足條件的F,對其指向的孩子結點進行FindLeaf操做,直到尋找到E或者全部條目均以被檢查過。

FL2[搜索葉子結點以找到記錄] 若是T是葉子結點,那麼檢查每個條目是否有E存在,若是有則返回T

 

FunctionCondenseTree

描述:L爲包含有被刪除條目的葉子結點。若是L的條目數過少(小於要求的最小值m),則必須將該葉子結點L從樹中刪除。通過這一刪除操做,L中的剩餘條目必須從新插入樹中。此操做將一直重複直至到達根結點。一樣,調整在此修改樹的過程所通過的路徑上的全部結點對應的矩形大小。

CT1[初始化] NL。初始化一個用於存儲被刪除結點包含的條目的鏈表Q

CT2[找到父條目] 若是N爲根結點,那麼直接跳轉至CT6。不然令PN 的父結點,令ENP結點中存儲的指向N的條目。

CT3[刪除下溢結點] 若是N含有條目數少於m,則從P中刪除EN,並把結點N中的條目添加入鏈表Q中。

CT4[調整覆蓋矩形] 若是N沒有被刪除,則調整EN.I使得其對應矩形可以剛好覆蓋N中的全部條目所對應的矩形。

CT5[向上一層結點進行操做] N等於P,從CT2開始重複操做。

CT6[從新插入孤立的條目] 全部在Q中的結點中的條目須要被從新插入。原來屬於葉子結點的條目可使用Insert操做進行從新插入,而那些屬於非葉子結點的條目必須插入刪除以前所在層的結點,以確保它們所指向的子樹還處於相同的層。

 

      R樹刪除記錄過程當中的CondenseTree操做是不一樣於B樹的。咱們知道,B樹刪除過程當中,若是出現結點的記錄數少於半滿(即下溢)的狀況,則直接把這些記錄與其餘葉子的記錄「融合」,也就是說兩個相鄰結點合併。然而R樹倒是直接從新插入。

 

一樣,咱們用圖直觀的說明這個操做。

 

假設結點最大條目數爲4,最小條目數爲2。在這張圖中,咱們的目標是刪除記錄c。首先使用FindLeaf操做找到c所處在的葉子結點的位置——R11。當cR11刪除時,R11就只有一條記錄了,少於最小條目數2,出現下溢,此時要調用CondenseTree操做。這樣,c被刪除,R11剩餘的條目——指向記錄d的指針——被插入鏈表Q。而後向更高一層的結點進行此操做。這樣R12會被插入鏈表中。原理是同樣的,在這裏就再也不贅述。

有一點須要解釋的是,咱們發現這個刪除操做向上傳遞以後,根結點的條目R1也被插入了Q中,這樣根結點只剩下了R2。彆着急,從新插入操做會有效的解決這個問題。咱們插入R3R12d至它原來所處的層。這樣,咱們發現根結點只有一個條目了,此時根據Inert中的操做,咱們把這個根結點刪除,它的孩子結點,即R5R6R7R3所在的結點被置爲根結點。至此,刪除操做結束。

 

結語

      R樹是一種可以有效進行高維空間搜索的數據結構,它已經被普遍應用在各類數據庫及其相關的應用中。但R樹的處理也具備侷限性,它的最佳應用範圍是處理26維的數據,更高維的存儲會變得很是複雜,這樣就不適用了。近年來,R樹也出現了不少變體,R*樹就是其中的一種。這些變體提高了R樹的性能,感興趣的讀者能夠參考相關文獻。文章有任何錯誤,還望各位讀者不吝賜教。本文完。

 

參考文獻以及推薦閱讀:

1.   Organization and Maintenance of Large Ordered Indices

2.   the ubiquitous B tree

3.   http://en.wikipedia.org/wiki/Btree (給出了國外一些開源地址)

4.   http://en.wikipedia.org/wiki/Btree#Technical_description

5.   http://cis.stvincent.edu/html/tutorials/swd/btree/btree.htmlinclude C++ source code

6.   http://slady.net/java/bt/view.php(若是瞭解了B-tree結構,該地址能夠在線對該結構進行查找(search),插入(insert),刪除(delete)操做。)
7. Guttman, A.; 「R-trees: a dynamic index structure for spatial searching,」 ACM, 1984, 14

8. http://www.cnblogs.com/CareySon/archive/2012/04/06/2435349.html

9. http://baike.baidu.com/view/298408.htm

10. http://www.cnblogs.com/leoo2sk/archive/2011/07/10/mysql-index.html (介紹了mysql中myisam和innodb這兩種引擎的內部索引機制,以及對不一樣字段的索引時,檢索效率上的對比,主要也是基於其內部機制的理解)

11. http://www.oschina.net/news/31988/mysql-indexing-best-practices (MySQL 索引最佳實踐);

12. http://idlebox.net/2007/stx-btree/(此頁面包含B樹生成構造的一些演示demo)。

相關文章
相關標籤/搜索