資料結構 關於B樹說明及插入和分裂
如演算法導論中的一個陣列,留於此用於以後的繼續深入學習。
B樹的定義和特點:
B樹的階實際上就是指向子樹的最大指標個數
比如2-3樹階為3,2-3-4樹階為4
B樹已經不是常規的樹結構,多用於檔案系統管理,每個節點可以有多個指向
孩子的指標,特點如下:
1、根要麼為空樹,要麼至少有2個子樹
2、假設M階的B樹,n個指向子樹的指標
則:
Ceil(M/2)<=n<=M
n-1個關鍵字
則
ceil(M/2)-1<=n-1<=M-1
3、所有葉子結點在同一層次
4、設Pn為指標,Kn為關鍵字
KEYS=n P0K1P1K2P2K3P3..........
P0指向子樹的所有值均小於K1
P1指向子樹的所有值均大於K1
5、有K1<K2<K3<K4的順序
如下就是一顆5階4關鍵字的B樹
![](https://i.iter01.com/images/c06d1e9b764bc0f4b5b021558227cb41d1c4969b4788d259f4f7d2a9fae3269e.png)
B樹的優勢(可以說也是B+樹的優勢):
由於在一些應用中比如資料庫應用中,資料量肯能非常大,在這種情況一棵完全依賴記憶體的
樹就不可取了,首先資料量過大使用AVL樹或者紅黑樹得到的深度難以想象,二來不可能有
那麼多的記憶體用於存放整個資料,實際上在資料庫中往往是通過一個指標(並非記憶體中的指標),
這個指標實際上是資料塊的塊號,如果我們只將B樹的根結點快取到記憶體中,那麼我們根據
B樹的查詢原則將路徑中的資料塊存放到快取,那麼不僅效能大大提高,同時也減少了記憶體
的使用,一般物理磁碟的讀取速度為毫秒級別,而記憶體矽儲存的讀取速度為納秒級別,基本
上是5個數量級的差別。同時為了更大發揮磁碟讀取的效率,一般來講在資料庫中B+樹的根結
點為1個block.
B樹的插入分裂:
1、如果葉子結點有足夠的空間如果按照嚴格的定義就是
ciel(M/2)-1<=n-1<M-1 注意是小於M-1這樣肯定小於
最大允許的關鍵字,那麼就在找到的葉子結點進行插入
2、如果葉子結點關鍵字大於M-1,而雙親結點空間<M-1,則
分裂葉子結點,同時中間元素上移到雙親結點(我們以奇數
關鍵字為例)的相應位置,這樣的移動的依據來自於
第四點:
KEYS=n P0K1P1K2P2K3P3..........
P0指向子樹的所有值均小於K1
P1指向子樹的所有值均大於K1
B樹實際上也是一個有序樹,按照這個規則上移後必然也滿足
上面的規則,因為在一個節點中資料是有序排列我們設定是
升序。
3、如果雙親結點也處於M-1狀態,那麼雙親結點也需要分裂,其規則
如葉子結點一致,關於這一點演示如下:
考慮如下樹(5階,4關鍵字,注意5階關子健最少2個最多4個)
插入55:
4、如果根結點也處於M-1狀態,上面的情況就出現了B樹索引高度加1的情況
演示如下:
考慮如下樹(5階,4關鍵字)插入84:
最後節點64分裂開來,樹的高度由3變為了4
![](https://i.iter01.com/images/e44f8caf2b5ac35bb6121b2f176159ae24a6cdfb1bcbe73cdfc10c554f9777bf.png)
演算法導論中的說明:
在演算法導論中對b樹的分裂做了一定的改動,也就是說在進行資料查詢的時候
路過的結點,如果發現出現了等於最大關鍵的節點就進行分裂,這樣雖然增加
了分裂的可能性,但是並不會增加太多因為增加的分裂次數只是一個常量而已
是一種方便的可行的程式設計方式。
並且進行了義一箇中間量用於標示節點指標的中間位置,如果設定這個中間為
_BTREE_POINTER_M_,那麼指標的個數最大始終為2*_BTREE_POINTER_M_為偶數,
而關鍵字個數始終為2*_BTREE_POINTER_M_-1為一個奇數,如果定義
BTREE_POINTER_M_=2 那麼就是2-3-4樹,這樣對程式設計也帶來了一定的方便,我們
也採用這種方式。而2-3樹這樣的定義是不能實現的。
程式碼(問題很多沒除錯過,演算法導論描述編寫而成),這部分可以參考演算法導論關於
B樹的描述
點選(此處)摺疊或開啟
-
head.h
-
#define _BTREE_POINTER_M_ 2
-
#define _BTREE_POINTER_ (_BTREE_POINTER_M_ * 2) //2-3-4tree。
-
#define _BTREE_KEYS_ (_BTREE_POINTER_M_ * 2-1)
-
#define bool int
-
#define true 1
-
#define false 0
-
#define ALLOCBNODE (BTNodeP)calloc(1,sizeof(BTNode))
-
#define SIZENODE sizeof(DULNODE)
-
#define SIZELISTHEAD sizeof(LISTHEAD)
-
#define SIZEBTHead sizeof(BTHead)
-
-
-
typedef struct dulnode //node type of btree data
-
{
-
-
int data; //example
-
struct dulnode *prior;
-
struct dulnode *next;
-
} DULNODE,*DULNODEP;
-
-
typedef struct listhead ///node type of btree data header
-
{
-
-
DULNODEP head;
-
DULNODEP last;
-
DULNODEP current;
-
int length;
-
} LISTHEAD,*LISTHEADP;
-
-
typedef struct BTNode{
-
int keynum; // 結點中關鍵字的個數,keynum <= _BTREE_KEYS_
-
LISTHEADP keyhder; // 資料連結串列實現的頭指標
-
struct BTNode* child[_BTREE_POINTER_]; // 孩子指標
-
bool isLeaf; // 是否是葉子節點的標誌
-
int nodenum; // 節點計數
-
}BTNode,*BTNodeP;
-
-
-
typedef struct BTHead{
-
BTNodeP root_node; //指向B樹的根結點
-
int max_node_num; // 當前節點個數計數
-
int btree_level; //B樹的層次
- }BTHead,*BTHeadP;
點選(此處)摺疊或開啟
-
#include<stdio.h>
-
#inlcude<stdlib.h>
-
-
-
//chain fun
-
-
bool initlist(LISTHEADP* p)
-
{
-
*p = (LISTHEADP)malloc(SIZELISTHEAD);
-
if(!*p)
-
{
-
return false;
-
}
-
else
-
{
-
memset(*p,0,SIZELISTHEAD);
-
(*p)->head = NULL;
-
(*p)->last = NULL;
-
(*p)->current = NULL;
-
(*p)->length = 0;
-
return true;
-
}
-
}
-
-
//inslast insert one node at last postion
-
-
void inslast(LISTHEADP h,DULNODEP s)
-
{
-
if(!(h->head)) //list is empty or not
-
{
-
h->head = s;
-
h->last = s;
-
h->current = s;
-
h->length++;
-
}
-
else
-
{
-
h->last->next = s;
-
s->prior = h->last;
-
h->last = s;
-
h->current = s;
-
h->length++;
-
}
-
}
-
-
void delfirst(LISTHEADP h) //delete first node current_point to next node
-
{
-
DULNODEP p;
-
if(!(h->head))
-
{
-
printf("error(1):delfirst() error list have no node!\n");
-
exit(1);
-
}
-
else if(!(h->head->next)) //only one node
-
{
-
free(h->head);
-
h->head = NULL;
-
h->current = NULL;
-
h->last = NULL;
-
h->length--;
-
}
-
else
-
{
-
p = h->head ;
-
h->head->next->prior = NULL;
-
h->head = h->head->next;
-
h->current = h->head;
-
h->length--;
-
free(p);
-
}
-
}
-
-
bool makenode(int datavalue,DULNODEP* p)
-
{
-
*p = (DULNODEP) malloc (SIZENODE);
-
if(!(*p))
-
{
-
return false;
-
}
-
else
-
{
-
memset(*p,0,SIZENODE);
-
(*p)->data = datavalue;
-
(*p)->next = NULL;
-
(*p)->prior = NULL;
-
return true;
-
}
-
}
-
-
static DULNODEP getelemp(const LISTHEADP h,int postion)
-
{
-
int i=0;
-
DULNODEP p;
-
if(postion > h->length || postion ==0 )
-
{
-
printf("error(2):getelemp() postion large than lenth or poastion = 0\n");
-
exit(2);
-
}
-
p = h->head;
-
-
while(i<postion-1)
-
{
-
i++;
-
p = p->next;
-
}
-
return p;
-
}
-
-
void dellast(LISTHEADP h) //delete last node current_point to prior node
-
{
-
DULNODEP p;
-
if(!(h->head))
-
{
-
printf("error(1):delfirst() error list have no node!\n");
-
exit(1);
-
}
-
else if(!(h->head->next)) //only one node
-
{
-
free(h->head);
-
h->head = NULL;
-
h->current = NULL;
-
h->last = NULL;
-
h->length--;
-
}
-
else
-
{
-
p = h->last ;
-
h->last->prior->next = NULL;
-
h->last = p->prior;
-
h->current = p->prior;
-
h->length--;
-
free(p);
-
}
-
}
-
-
-
static int findpos(LISTHEADP h,int k,int i)
-
{
-
DULNODEP p=NULL;
-
-
if(h->length == 0)
-
{return 1;}
-
else
-
{
-
p = h->last;
-
while(i>=1 && p->data > k) // exp : i = 1 one key if k<data frist insert else k>data return 2
-
{
-
if(i==1)
-
{
-
return i;
-
}
-
p = p->prior;
-
i--;
-
}
-
return i+1; // i+1=2 is insert after node 1
-
}
-
}
-
-
-
//addnode add one node after give postion
-
void addnode(DULNODEP inode,int postion,LISTHEADP h) //insert one elem after postion
-
{
-
DULNODEP p;
-
p = getelemp(h,postion);
-
if(!p->next) //last node?
-
{
-
p->next = inode;
-
inode->prior = p;
-
inode->next = NULL;
-
h->last = inode;
-
h->current = inode;
-
}
-
else
-
{
-
inode->prior = p;
-
inode->next = p->next;
-
p->next->prior = inode;
-
p->next = inode;
-
h->current = inode;
-
}
-
h->length++;
-
}
-
-
//insfirst insert one node at first postion
-
-
void insfirst(LISTHEADP h,DULNODEP s)
-
{
-
if(!(h->head)) //list is empty or not
-
{
-
h->head = s;
-
h->last = s;
-
h->current = s;
-
h->length++;
-
}
-
else
-
{
-
h->head->prior = s;
-
s ->next = h->head;
-
h->head = s;
-
h->current = s;
-
h->length++;
-
-
}
-
-
}
-
-
-
//btree fun
-
-
bool B_Tree_Inital(BTHeadP* p)
-
{
-
*p = (BTHeadP)malloc(SIZEBTHead);
-
if(!*p)
-
{
-
return false;
-
}
-
else
-
{
-
memset(*p,0,SIZEBTHead);
-
(*p)->root_node = NULL;
-
(*p)->max_node_num = 0;
-
(*p)->btree_level = 0;
-
return true;
-
}
-
}
-
-
-
void B_Tree_Create(BTNodeP* root,BTHeadP* p)
-
{
-
BTNodeP x = NULL;
-
if(!(x = ALLOCBNODE))
-
{
-
printf("B_Tree_Create error mem error(10)\n");
-
exit(10);
-
}
-
(*root) = x;
-
(*p)->root_node = (*root);//head pointer is root
-
(*p)->max_node_num++;
-
(*p)->btree_level++;
-
(x)->isLeaf = true;
-
(x)->keynum = 0;
-
(x)->nodenum = (*p)->max_node_num;
-
if(!(initlist(&(x->keyhder)));
-
{
-
printf("B_Tree_Create error mem error(11)\n");
-
exit(11);
-
}
-
-
}
-
-
void B_Tree_Split(BTNodeP* x,int i,BTNodeP* y,BTHeadP* p) //X是上層結點,y是X的一個滿的子節點,i為y中間元素上浮到x中的位置
-
{
-
BTNodeP z = NULL;
-
DULNODEP zcnode = NULL; //btree node z's chain node pointer;
-
DULNODEP yfnode = NULL; //used find any node pointer;
-
int j=1;
-
if(!(z = ALLOCBNODE))
-
{
-
printf("B_Tree_Split error mem error(12)\n");
-
exit(12);
-
}
-
(*p)->max_node_num++;
-
z->isLeaf = (*y)->isLeaf;
-
z->keynum = _BTREE_POINTER_M_ - 1;
-
z->nodenum = (*p)->max_node_num++;
-
if(!(initlist(&(z->keyhder)));
-
{
-
printf("B_Tree_Split error mem error(13)\n");
-
exit(13);
-
}
-
yfnode = getelemp((*y)->keyhder,_BTREE_POINTER_M_+1);
-
-
for(j=1;j<=_BTREE_POINTER_M_-1;j++) //z first half key = y last half key this is very import
-
/*
-
--x--
-
--y-- --z--
-
*/
-
{
-
makenode(yfnode->data,&zcnode);
-
inslast(z->keyhder,zcnode);
-
yfnode = yfnode->next;
-
}
-
for(j=1;j<=_BTREE_POINTER_M_-1;j++)//delete y last half key beacuase the key is give to z
-
{
-
dellast((*y)->keyhder);
-
}
-
if(!((*y)->isLeaf)) // if node y is not leaf node,child pointer must change ,give half pointer to z ,but total pointer not change
-
{
-
for(j=1;j<=_BTREE_POINTER_M_;j++)
-
{
-
z->child[j-1] = (*y)->child[j-1+_BTREE_POINTER_M_];
-
(*y)->child[j-1+_BTREE_POINTER_M_] = NULL;
-
}
-
}
-
(*y)->keynum = _BTREE_POINTER_M_ - 1; //key change
-
for(j=(*x)->keynum+1;j>=i+1;j-- ) // 0 1 1 <insert new i=2 is before 2> 2 2 3 3 4 4 --> 0 1 1 new2 new2 3 3 4 4 5 5 i now is before i
-
{
-
(*x)->child[j] = (*x)->child[j-1];
-
}
-
(*x)->child[i] = z;
-
-
//find y last data is split data,use yfnode to store
-
makenode((*y)->keyhder->last->data,&yfnode);
-
//if sucess delete last y data
-
dellast((*y)->keyhder) ;
-
-
if(i == 1) //move key value
-
{
-
insfirst((*x)->keyhder,yfnode);
-
}
-
else
-
{
-
addnode(yfnode,i-1,(*x)->keyhder);
-
}
-
(*x)->keynum ++;
-
return yfnode->data;
-
-
}
-
-
void B_Tree_Insert_Nofull(BTNodeP* x,int k,BTHeadP* p)
-
{
-
int i ;
-
int pos;
-
int dataret;
-
DULNODEP np = NULL;
-
i = (*x)->keynum;
-
makenode(k,&np);
-
-
if((*x)->isLeaf) //if the x is leaf node ?
-
{
-
pos=findpos((*x)->keyhder,k,i);
-
if(pos == 1)
-
{
-
insfirst((*x)->keyhder,np);// pos == 1 insert at first
-
}
-
else
-
{
-
addnode(np,pos-1,(*x)->keyhder); //addnode is insert after pos so pos-1
-
}
-
(*x)->keynum++
-
}
-
else
-
{
-
pos=findpos((*x)->keyhder,k,i); //not leaf node find leaf node
-
if(((*x)->child)[pos-1]->keynum == _BTREE_KEYS_ ) // is child leaf node is full split it
-
{
-
-
dataret=B_Tree_Split(x,pos,&(((*x)->child)[pos-1]),p); //key real insert pos
-
if( k > dataret) //split sucess if k> B_TREE_SPLIS RETURN SPLIT DATA
-
{
-
pos=pos+1;
-
}
-
}
-
B_Tree_Insert_Nofull(&(((*x)->child)[pos-1]),k); //key pos is 1 2 3 4 5......pointer pos is 0 1 2 3 4....so pos-1
-
}
-
}
-
-
-
void B_Tree_Insert(BTHeadP* p,int k) //k is value to insert ,BtheadP has a pointer to b_tree_root
-
{
-
BTNodeP r = (*p)->root_node;
-
BTNodeP s = NULL;
-
if(r->keynum = _BTREE_KEYS_)
-
{
-
if(!(s = ALLOCBNODE))// new root node
-
{
-
printf("B_Tree_Insert error mem error(14)\n");
-
exit(14);
-
}
-
(*p)->max_node_num++;
-
(*p)->root_node = s;
-
(*p)->btree_level++;
-
s->isLeaf = FALSE;
-
s->keynum = 0;
-
s->child[0] = r;
-
s->nodenum = (*p)->max_node_num;
-
if(!(initlist(&(s->keyhder)));
-
{
-
printf("B_Tree_Insert error mem error(15)\n");
-
exit(15);
-
}
-
B_Tree_Split(&s,1,&r,&p);
-
B_Tree_Insert_Nofull(&s,k,p);
-
}
-
else
-
{
-
B_Tree_Insert_Nofull(&r,k,p);
-
}
- }
關於B樹的刪除會涉及到更多的複雜的方面,比如普通刪除,比如節點融合,B樹高度的
降低等,我沒有仔細的學習和研究。
可見B樹B+樹這種資料結構還是比較負載,如果加上很多很多的其他的連結串列或者資料結構
那麼編寫程式的難度非常大,我們使用資料庫的DBA們應該為資料庫軟體的開發者們心存
敬畏,他們是天才的程式設計師。
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/7728585/viewspace-2126929/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 【資料結構】B樹、B+樹詳解資料結構
- 資料結構之「B樹」資料結構
- 資料結構之「B+樹」資料結構
- Java關於資料結構的實現:樹Java資料結構
- 淺析B-樹分裂
- 資料結構——樹和森林資料結構
- 關於資料結構資料結構
- 關於樹的資料結構(二分搜尋樹,堆和優先佇列)資料結構佇列
- 【PG結構】Postgresql資料庫資料目錄說明SQL資料庫
- 資料結構之MySQL獨愛B+樹(二叉樹、AVL樹、紅黑樹、B樹對比)資料結構MySql二叉樹
- 資料結構之查詢(順序、折半、分塊查詢,B樹、B+樹)資料結構
- 【譯】資料結構中關於樹的一切(java版)資料結構Java
- 資料結構——樹資料結構
- 資料結構-樹資料結構
- 雜湊,二叉樹,紅黑樹,B樹,B+樹,LSM樹等資料結構做索引比較二叉樹資料結構索引
- 資料結構之樹結構概述(含滿二叉樹、完全二叉樹、平衡二叉樹、二叉搜尋樹、紅黑樹、B-樹、B+樹、B*樹)資料結構二叉樹
- Mysql索引資料結構為什麼是B+樹?MySql索引資料結構
- Linux系統結構說明及用途介紹Linux
- 資料結構—-連結串列的增和插入(2018/10/23)資料結構
- DKhadoop框架結構說明Hadoop框架
- 微機結構說明
- flowable 表結構說明
- 關於elementUI樹狀結構的bugUI
- 重學資料結構(六、樹和二叉樹)資料結構二叉樹
- 重學資料結構之樹和二叉樹資料結構二叉樹
- 『資料結構』樹(Tree)資料結構
- 資料結構-字典樹資料結構
- 資料結構之「樹」資料結構
- 資料結構 - 樹,初探資料結構
- 資料結構 - AVL 樹資料結構
- 前端資料結構--樹前端資料結構
- 關於Mysql索引的資料結構MySql索引資料結構
- WordPress採集入庫表結構關聯說明
- 七、基本資料結構(樹形結構)資料結構
- 資料結構學習之樹結構資料結構
- 資料結構(樹):二叉樹資料結構二叉樹
- Docker 關鍵字說明及一鍵構建相關服務Docker
- 資料結構中樹和森林的區別資料結構
- 高階資料結構---堆樹和堆排序資料結構排序