數據結構之B-樹,你天天都在用的,源碼發佈!

 

     五一前就籌劃着寫下這篇文章,可是迫於本身歷來沒有實現過B-樹(若是你們感興趣,我能夠考慮寫一篇B+樹的文章),手中沒有源代碼,另外本身之前對B-樹也是隻知其一;不知其二狀態中,擔憂誤人子弟,在4月30日終於把代碼寫完,今天調完以前的bug以後,那種感受就像在鳥無人煙的大荒漠中走了很久,看到一間有水的屋子,長舒一口氣!好的廢話很少說,下面直接切入正題!html

     鏈表,樹,圖是最基本的數據結構了,鏈表有單鏈表、雙鏈表,有環和無環等等;樹有二叉樹、多叉樹,平衡樹、不平衡樹等等;圖有有向、無向圖等等。若是把算法建模當作是建築師建造一座大廈,那麼數據結構就是地基,地基的好壞直接關係到房子能建多高。node

     今天咱們要講到的是樹的一種,即B-樹。要說B-樹,就必然要說平衡二叉樹,不然這是不科學的,沒有平衡二叉樹哪來B-樹。它的定義是這樣的:web

     平衡二叉樹(Balanced Binary Tree)又被稱爲AVL樹(有別於AVL算法),且具備如下性質:它是一 棵空樹或它的左右兩個子樹的高度差的絕對值不超過1,而且左右兩個子樹都是一棵平衡二叉樹。算法

     

 

圖一數據庫

     這個是一顆典型的平衡二叉樹,對根節點50而言,左右子樹的高度都是3,高度差絕對值不超過1;對左子樹而言,根節點是17,可是他的左右兩子樹高度都是2,差的絕對值也不超過1.咱們遞歸的觀察,顯然它符合平衡二叉樹的基本條件。數據結構

    若是咱們把右子樹剪掉,他就不是平衡二叉樹了。左邊高度爲3,右邊高度爲0;3-0=3>1.學習

圖二大數據

 OK,說到這裏,我想咱們能夠講講B-樹了。B-樹其實能夠理解爲多叉平衡樹!它的定義是這樣的:(其中T通常大於3)ui

 1)不只高度平衡,並且全部的葉子節點都在同一層。spa

 2)除根節點外其餘每一個節點最少保存(T-1)個關鍵字,至多保存(2T-1)個關鍵字;至少保存(T)個孩子結點,至多(2T)個孩子結點。

 3)對於關鍵字K而言,左孩子結點的全部關鍵字都小於K,右孩子結點的全部關鍵字都大於K。在同一個結點上,關鍵字是從小到大依次排列。結點中除葉子節點關鍵字外,均有左右孩子。

隨便提一下,如今有不少B-樹的變形,有些要求每一個節點至少保存2/3(2T-1)個關鍵字等等,也是能夠的。

 以下圖就是一顆典型的B-樹:

圖三

      OK,對於B-樹,想必你們有了一個直觀的認識。那麼B-樹有什麼用呢?當今社會太浮躁了,沒用的東西留着幹嗎?果斷棄之。顯然B-樹是很是有用的。你們用數據庫進行開發嗎?若是你用,那麼其實你天天都在用B-樹有沒有,由於大部分數據庫的底層實現都是用B-樹。

      B-樹對大數據特別有用,特別是查詢狀況多的時候(好比數據庫);由於一旦數據量特別大以後,內存確定不能把全部數據都裝到內存中,咱們惟有經過硬盤存儲,存儲在硬盤中以後,怎樣才能高效的進行查詢呢?咱們仔細觀察B-絕對是一個很好的選擇,由於它最糟糕的時候效率都是logT(N);硬盤的速度相對內存來講是比較慢的,較少一次硬盤讀取對用戶體驗是一個莫大的支持!

   老規矩:若是須要所有源代碼點贊後留下email地址。

    其實基於B-的數據庫,數據操做基本能夠概括爲四個部分:

   讀取硬盤數據:根據給定頁面Id讀取硬盤數據到內存,而後返回內存相應位置;

   硬盤寫操做:給定內存需寫入數據地址信息,寫入到給定頁面id硬盤中。

   申請硬盤頁面:若是頁面中id不存在,開拓硬盤頁面,而後返回內存地址。

   釋放頁面:給定一個硬盤頁面id,釋放該資源。

   OK,咱們瞭解到了B-樹的重要性以後,也就知道了學習B-樹的現實意義了。那咱們如今能夠繼續開扒了。

   B-樹應該怎麼設計呢?看了B-樹的定義以後,B-樹數據結構應該包括:1)關鍵字個數,2)指向父節點的指針,3)關鍵字集合,4)孩子結點集合。

   根據以上論述,咱們把B-樹的數據結構代碼化以後以下:

 1 struct BTreeNode{  2     int keyNum;//關鍵字個數。
 3     BTreeNode* parentNode;//父結點指針
 4     vector<NodeValueType> nodeValues;  5     vector<BTreeNode*> childs;  6  BTreeNode(){  7         keyNum=0;  8         parentNode=NULL;  9  } 10 };

    對於B-樹而言最經常使用的就是增刪改查了。改能夠認爲是刪,而後增。因此能夠理解成主要操做有增、刪、查!下面分三部分詳解他們。

B-樹的查詢操做:

     B-樹的查詢能夠從根節點開始查找,根節點沒有查到,再查找某一個孩子結點。

     由於B-樹結點都有不少孩子節點,查找哪一個孩子結點就顯得特別重要了,總不能亂查吧。它應該是查找第一個比關鍵字大的左結點,若是沒有一個比該關鍵字大的關鍵字,則查找最右孩子。如此遞歸。直到查到葉子結點,若是葉子結點也沒有找到,那麼B-樹中不存在該關鍵字。

    好比對於圖三,查找關鍵字G。首先在根節點中查找,根節點只有M一個關鍵字,顯然不符合條件。查找到第一個大於G的關鍵字,這裏M比G大,全部積蓄查找M的左子樹,左子樹的根節點關鍵字有:D、H。一樣第一個比G大的是H。H的左孩子結點關鍵字是F、G。這樣,G被找到了。返回。

    

圖四

 算法具體描述爲:

 1 bool SearchBTreeByValue(BTreeNode* q,NodeValueType k){  2     int i=0;  3     while(i<q->keyNum&&q->nodeValues[i]<=k){  4         if (q->nodeValues[i]==k){  5             return true;//相等則返回q
 6  }  7         i++;  8  }  9     if (!q->childs.size()){ 10         return false;//沒有找到直接返回NULL
11  } 12     return SearchBTreeByValue(q->childs[i],k); 13 }

 B-樹的增長(插入)操做

    【插入操做要尋找到葉子節點插入】由於B-樹中最多隻能保存2T-1個關鍵字,若是當前的關鍵字個數已經達到2T-1,還需插入一個的話就須要分裂成兩個結點。以下圖,T=2.若是還需插入一個18.因爲結點X已經有3個關鍵字了。已經full了。若是還須要插入18,就不符合條件了,就須要分裂成兩個結點,同時增長一層。再進行插入。也能夠插入完成後,再分裂。最終仍是要分裂。

    若是B-樹沒有滿的話,直接插入就好。

     

圖五

 

 

圖六 

 1 BTreeNode* InsertNodeToBTreeByValue(BTreeNode* p,NodeValueType theValue){  2     if (SearchBTreeByValue(p,theValue)){  3         cout<<theValue<<"已經存在了,騷年!"<<endl;  4         return p;  5  }  6     while(p->childs.size()!=0){//一直找到葉子結點
 7         int i=0;  8         while(i<p->keyNum&&p->nodeValues[i]<theValue){  9             i++; 10  } 11         p=p->childs[i]; 12  } 13     p->nodeValues=InsertKeyToNodeByValue(p->nodeValues,theValue);//插入到當前葉子結點
14     p->keyNum++; 15     return adjustBTree(p);//使得最大不超過maxKey
16 }

 

B-樹的刪除操做

    說實話,刪除操做比增長操做複雜多了,這也是個人程序一直出現bug的地方。假設要在結點x中刪除關鍵字k。

    由於刪除操做須要考慮刪除後結點會少於T-1和樹不平衡的狀況。

    刪除操做主要考慮三種狀況:

    1)x是葉子結點,包含k

    2)x是內部結點,包含k

    3)x是內部結點,不包含k.

    對於1),直接刪除,同時更新keynum;

    對於2),考慮k的左右孩子,

                如有一個孩子的關鍵字個數大於T-1,若左孩子y,用最右邊的關鍵字和k交換,同時遞歸刪除delete(y,k);

                若右孩子(z)數大於T-1,則遞歸刪除delete(z,k);

                若左右孩子都等於T-1,則須要合併左孩子,k,右孩子,同時刪除右孩子。

   對於3),y=x.child[k];若y的關鍵字個數大於T-1,則遞歸刪除delete(y,k)

                                   若y的關鍵字個數等於T-1,則尋找他的兄弟節點,兄弟節點如有大於T-1個數的,補一個過來。

                                                                      若沒有的話,就進行和其中兄弟節點合併,再遞歸delete.

  1 BTreeNode* DeleteBTreeNode(BTreeNode* p,NodeValueType theValue){
  2     int theValueOrderInNode=GetOrderInNodeByValue(p,theValue);//返回當前序號,theValueOrderInNode
  3     //BTreeNode* w=p->parentNode;
  4     if (theValueOrderInNode!=-1&&p->childs.size()==0){
  5         p->nodeValues.erase(p->nodeValues.begin()+theValueOrderInNode,p->nodeValues.begin()+theValueOrderInNode+1);
  6         p->keyNum--;
  7         return GetBTreeRoot(p);
  8     }else if (theValueOrderInNode==-1){//該結點中不存在theValue
  9         int i=0;
 10         while(i<p->keyNum&&p->nodeValues[i]<theValue){
 11             i++;//獲得第I個孩子。
 12         }
 13         BTreeNode* y=p->childs[i];//找到i+1個孩子結點
 14         if (y->keyNum>atLeastKeyNum){
 15             return DeleteBTreeNode(y,theValue);
 16         }else if(y->keyNum==atLeastKeyNum){
 17             BTreeNode* leftSlibing=new BTreeNode(),*rightSlibing=new BTreeNode();//左右結點出現啦!
 18             leftSlibing=NULL,rightSlibing=NULL;
 19             if (i!=0){//有左兄弟
 20                 leftSlibing=p->childs[i-1];
 21             }
 22             if (i!=p->keyNum){//有右兄弟
 23                 rightSlibing=p->childs[i+1];
 24             }
 25             if (leftSlibing!=NULL&&leftSlibing->keyNum>atLeastKeyNum){//左兄弟移動一個到y中
 26                 y->nodeValues.insert(y->nodeValues.begin(),p->nodeValues[i-1]);
 27                 y->keyNum++;
 28                 p->nodeValues[i-1]=leftSlibing->nodeValues[leftSlibing->keyNum-1];
 29                 if (y->childs.size()!=0){
 30                     y->childs.insert(y->childs.begin(),*(leftSlibing->childs.end()-1));
 31                     leftSlibing->childs.erase(leftSlibing->childs.end()-1,leftSlibing->childs.end());
 32                 }
 33                 leftSlibing->nodeValues.erase(leftSlibing->nodeValues.end()-1,leftSlibing->nodeValues.end());
 34                 leftSlibing->keyNum--;
 35                 return DeleteBTreeNode(y,theValue);
 36             }else if(rightSlibing!=NULL&&rightSlibing->keyNum>atLeastKeyNum){//右兄弟移動一個到y中
 37                 y->nodeValues.insert(y->nodeValues.end(),p->nodeValues[i]);
 38                 y->keyNum++;
 39                 if (y->childs.size()!=0){
 40                     y->childs.insert(y->childs.end(),*(rightSlibing->childs.begin()));
 41                     rightSlibing->childs.erase(rightSlibing->childs.begin(),rightSlibing->childs.begin()+1);
 42                 }
 43                 p->nodeValues[i]=rightSlibing->nodeValues[0];
 44                 rightSlibing->nodeValues.erase(rightSlibing->nodeValues.begin(),rightSlibing->nodeValues.begin()+1);
 45                 rightSlibing->keyNum--;
 46                 return DeleteBTreeNode(y,theValue);
 47             }else{//合併成一個
 48                 if (leftSlibing!=NULL){//同左合併
 49                     leftSlibing->nodeValues.insert(leftSlibing->nodeValues.end(),p->nodeValues[i-1]);
 50                     leftSlibing->keyNum++;
 51                     p->nodeValues.erase(p->nodeValues.begin()+i-1,p->nodeValues.begin()+i);
 52                     MoveBTreeNode(leftSlibing,y);
 53                     p->childs.erase(p->childs.begin()+i-1,p->childs.begin()+i);
 54                     p->keyNum--;
 55                     if (p->keyNum==0){
 56                         leftSlibing->parentNode=NULL;
 57                     }
 58                     return DeleteBTreeNode(leftSlibing,theValue);
 59                 }else{//同右合併
 60                     y->nodeValues.insert(y->nodeValues.end(),p->nodeValues[i]);
 61                     y->keyNum++;
 62                     p->nodeValues.erase(p->nodeValues.begin()+i,p->nodeValues.begin()+i+1);
 63                     MoveBTreeNode(y,rightSlibing);
 64                     p->childs.erase(p->childs.begin()+i+1,p->childs.begin()+i+2);
 65                     p->keyNum--;
 66                     if (p->keyNum==0){
 67                         y->parentNode=NULL;
 68                     }
 69                     return DeleteBTreeNode(y,theValue);
 70                 }
 71             }
 72         }
 73     }else if (theValueOrderInNode!=-1){//在該結點中找到了。
 74         BTreeNode* leftChild=GetPreChildByNodeValue(p,theValue);
 75         BTreeNode* rightChild=GetSuccessByNodeValue(p,theValue);
 76         if (leftChild!=NULL&&leftChild->keyNum>atLeastKeyNum){//
 77             p->nodeValues[theValueOrderInNode]=leftChild->nodeValues[leftChild->keyNum-1];
 78             leftChild->nodeValues[leftChild->keyNum-1]=theValue;
 79             //leftChild->parentNode=p;
 80             return DeleteBTreeNode(leftChild,theValue);
 81         }
 82         else if (rightChild!=NULL&&rightChild->keyNum>atLeastKeyNum){//
 83             p->nodeValues[theValueOrderInNode]=rightChild->nodeValues[0];
 84             rightChild->nodeValues[0]=theValue;
 85             //rightChild->parentNode=p;
 86             return DeleteBTreeNode(rightChild,theValue);
 87         } 
 88         else if (leftChild!=NULL){//須要合併了
 89             leftChild->nodeValues.insert(leftChild->nodeValues.end(),p->nodeValues[theValueOrderInNode]);
 90             leftChild->keyNum++;
 91             MoveBTreeNode(leftChild,rightChild);//從左移到右
 92             p->nodeValues.erase(p->nodeValues.begin()+theValueOrderInNode,p->nodeValues.begin()+theValueOrderInNode+1);
 93             p->childs.erase(p->childs.begin()+theValueOrderInNode+1,p->childs.begin()+theValueOrderInNode+2);
 94             p->keyNum--;
 95             if (p->keyNum==0){
 96                 leftChild->parentNode=NULL;
 97             }
 98             return DeleteBTreeNode(leftChild,theValue);
 99         }
100     }
101     return NULL;
102 }

 

操做數據:用106,103,109,130,145,165,42,60,136,107,108對B-樹進行初始化。而後再進行查找刪除操做。

                        圖七

 參考文獻:http://zgking.com:8080/home/donghui/publications/books/dshandbook_BTree.pdf

                http://webdocs.cs.ualberta.ca/~holte/T26/del-b-tree.html

                http://www.cs.nott.ac.uk/~nza/G52ADS/btrees2.pdf

 

      版權全部,歡迎轉載,可是轉載請註明出處:瀟一

相關文章
相關標籤/搜索