B-tree(C++實現)
阿新 • • 發佈:2018-12-25
#pragma once template<class T> class CBTree { private: static const int M = 3; //B樹的最小度數 static const int KEY_MAX = 2*M-1; //節點包含關鍵字的最大個數 static const int KEY_MIN = M-1; //非根節點包含關鍵字的最小個數 static const int CHILD_MAX = KEY_MAX+1; //孩子節點的最大個數 static const int CHILD_MIN = KEY_MIN+1; //孩子節點的最小個數 struct Node { bool isLeaf; //是否是葉子節點 int keyNum; //節點包含的關鍵字數量 T keyValue[KEY_MAX]; //關鍵字的值陣列 Node *pChild[CHILD_MAX]; //子樹指標陣列 Node(bool b=true, int n=0) :isLeaf(b), keyNum(n){} }; public: CBTree() { m_pRoot = NULL; //建立一棵空的B樹 } ~CBTree() { clear(); } bool insert(const T &key) //向B數中插入新結點key { if (contain(key)) //檢查該關鍵字是否已經存在 { return false; } else { if (m_pRoot==NULL)//檢查是否為空樹 { m_pRoot = new Node(); } if (m_pRoot->keyNum==KEY_MAX) //檢查根節點是否已滿 { Node *pNode = new Node(); //建立新的根節點 pNode->isLeaf = false; pNode->pChild[0] = m_pRoot; splitChild(pNode, 0, m_pRoot); m_pRoot = pNode; //更新根節點指標 } insertNonFull(m_pRoot, key); return true; } } bool remove(const T &key) //從B中刪除結點key { if (!search(m_pRoot, key)) //不存在 { return false; } if (m_pRoot->keyNum==1)//特殊情況處理 { if (m_pRoot->isLeaf) { clear(); return true; } else { Node *pChild1 = m_pRoot->pChild[0]; Node *pChild2 = m_pRoot->pChild[1]; if (pChild1->keyNum==KEY_MIN&&pChild2->keyNum==KEY_MIN) { mergeChild(m_pRoot, 0); deleteNode(m_pRoot); m_pRoot = pChild1; } } } recursive_remove(m_pRoot, key); return true; } void display()const //列印樹的關鍵字 { displayInConcavo(m_pRoot,KEY_MAX*10); } bool contain(const T &key)const //檢查該key是否存在於B樹中 { return search(m_pRoot, key); } void clear() //清空B樹 { recursive_clear(m_pRoot); m_pRoot = NULL; } private: //刪除樹 void recursive_clear(Node *pNode) { if (pNode!=NULL) { if (!pNode->isLeaf) { for(int i=0; i<=pNode->keyNum; ++i) recursive_clear(pNode->pChild[i]); } deleteNode(pNode); } } //刪除節點 void deleteNode(Node *&pNode) { if (pNode!=NULL) { delete pNode; pNode = NULL; } } //查詢關鍵字 bool search(Node *pNode, const T &key)const { if (pNode==NULL) //檢測節點指標是否為空,或該節點是否為葉子節點 { return false; } else { int i; for (i=0; i<pNode->keyNum && key>*(pNode->keyValue+i); ++i)//找到使key<=pNode->keyValue[i]成立的最小下標i { } if (i<pNode->keyNum && key==pNode->keyValue[i]) { return true; } else { if (pNode->isLeaf) //檢查該節點是否為葉子節點 { return false; } else { return search(pNode->pChild[i], key); } } } } //分裂子節點 void splitChild(Node *pParent, int nChildIndex, Node *pChild) { //將pChild分裂成pLeftNode和pChild兩個節點 Node *pRightNode = new Node();//分裂後的右節點 pRightNode->isLeaf = pChild->isLeaf; pRightNode->keyNum = KEY_MIN; int i; for (i=0; i<KEY_MIN; ++i)//拷貝關鍵字的值 { pRightNode->keyValue[i] = pChild->keyValue[i+CHILD_MIN]; } if (!pChild->isLeaf) //如果不是葉子節點,拷貝孩子節點指標 { for (i=0; i<CHILD_MIN; ++i) { pRightNode->pChild[i] = pChild->pChild[i+CHILD_MIN]; } } pChild->keyNum = KEY_MIN; //更新左子樹的關鍵字個數 for (i=pParent->keyNum; i>nChildIndex; --i)//將父節點中的nChildIndex後的所有關鍵字的值和子樹指標向後移一位 { pParent->pChild[i+1] = pParent->pChild[i]; pParent->keyValue[i] = pParent->keyValue[i-1]; } ++pParent->keyNum; //更新父節點的關鍵字個數 pParent->pChild[nChildIndex+1] = pRightNode; //儲存右子樹指標 pParent->keyValue[nChildIndex] = pChild->keyValue[KEY_MIN];//把節點的中間值提到父節點 } //在非滿節點中插入關鍵字 void insertNonFull(Node *pNode, const T &key) { int i = pNode->keyNum; //獲取節點內關鍵字個數 if (pNode->isLeaf) //pNode是葉子節點 { while (i>0&&key<pNode->keyValue[i-1]) //從後往前,查詢關鍵字的插入位置 { pNode->keyValue[i] = pNode->keyValue[i-1]; //向後移位 --i; } pNode->keyValue[i] = key; //插入關鍵字的值 ++pNode->keyNum; //更新節點關鍵字的個數 } else//pNode是內節點 { while(i>0&&key<pNode->keyValue[i-1]) //從後往前,查詢關鍵字的插入的子樹 --i; Node *pChild = pNode->pChild[i]; //目標子樹結點指標 if (pChild->keyNum==KEY_MAX) //子樹節點已滿 { splitChild(pNode, i, pChild);//分裂子樹節點 if(key>pNode->keyValue[i]) //確定目標子樹 pChild = pNode->pChild[i+1]; } insertNonFull(pChild, key); //插入關鍵字到目標子樹節點 } } //用括號列印樹 void displayInConcavo(Node *pNode, int count)const { if (pNode!=NULL) { int i, j; for (i=0; i<pNode->keyNum; ++i) { if (!pNode->isLeaf) { displayInConcavo(pNode->pChild[i], count-2); } for (j=count; j>=0; --j) { cout<<"-"; } cout<<pNode->keyValue[i]<<endl; } if (!pNode->isLeaf) { displayInConcavo(pNode->pChild[i], count-2); } } } //合併兩個子節點 void mergeChild(Node *pParent, int index) { Node *pChild1 = pParent->pChild[index]; Node *pChild2 = pParent->pChild[index+1]; //將pChild2資料合併到pChild1 pChild1->keyNum = KEY_MAX; pChild1->keyValue[KEY_MIN] = pParent->keyValue[index];//將父節點index的值下移 int i; for (i=0; i<KEY_MIN; ++i) { pChild1->keyValue[i+KEY_MIN+1] = pChild2->keyValue[i]; } if (!pChild1->isLeaf) { for (i=0; i<CHILD_MIN; ++i) { pChild1->pChild[i+CHILD_MIN] = pChild2->pChild[i]; } } //父節點刪除index的key,index後的往前移一位 --pParent->keyNum; for(i=index; i<pParent->keyNum; ++i) { pParent->keyValue[i] = pParent->keyValue[i+1]; pParent->pChild[i+1] = pParent->pChild[i+2]; } deleteNode(pChild2); //刪除pChild2 } //遞迴的刪除關鍵字 void recursive_remove(Node *pNode, const T &key) { int i=0; while(i<pNode->keyNum&&key>pNode->keyValue[i]) ++i; if (i<pNode->keyNum&&key==pNode->keyValue[i])//關鍵字key在節點pNode中 { if (pNode->isLeaf)//pNode是個葉節點 { //從pNode中刪除k --pNode->keyNum; for (; i<pNode->keyNum; ++i) { pNode->keyValue[i] = pNode->keyValue[i+1]; } return; } else//pNode是個內節點 { Node *pChildPrev = pNode->pChild[i];//節點pNode中前於key的子節點 Node *pChildNext = pNode->pChild[i+1];//節點pNode中後於key的子節點 if (pChildPrev->keyNum>=CHILD_MIN)//節點pChildPrev中至少包含CHILD_MIN個關鍵字 { T prevKey = getPredecessor(pChildPrev); //獲取key的前驅關鍵字 recursive_remove(pChildPrev, prevKey); pNode->keyValue[i] = prevKey; //替換成key的前驅關鍵字 return; } else if (pChildNext->keyNum>=CHILD_MIN)//節點pChildNext中至少包含CHILD_MIN個關鍵字 { T nextKey = getSuccessor(pChildNext); //獲取key的後繼關鍵字 recursive_remove(pChildNext, nextKey); pNode->keyValue[i] = nextKey; //替換成key的後繼關鍵字 return; } else//節點pChildPrev和pChildNext中都只包含CHILD_MIN-1個關鍵字 { mergeChild(pNode, i); recursive_remove(pChildPrev, key); } } } else//關鍵字key不在節點pNode中 { Node *pChildNode = pNode->pChild[i];//包含key的子樹根節點 if (pChildNode->keyNum==KEY_MIN)//只有t-1個關鍵字 { Node *pLeft = i>0 ? pNode->pChild[i-1] : NULL; //左兄弟節點 Node *pRight = i<pNode->keyNum ? pNode->pChild[i+1] : NULL;//右兄弟節點 int j; if (pLeft&&pLeft->keyNum>=CHILD_MIN)//左兄弟節點至少有CHILD_MIN個關鍵字 { //父節點中i-1的關鍵字下移至pChildNode中 for (j=pChildNode->keyNum; j>0; --j) { pChildNode->keyValue[j] = pChildNode->keyValue[j-1]; } pChildNode->keyValue[0] = pNode->keyValue[i-1]; if (!pLeft->isLeaf) { for (j=pChildNode->keyNum+1; j>0; --j) //pLeft節點中合適的子女指標移植到pChildNode中 { pChildNode->pChild[j] = pChildNode->pChild[j-1]; } pChildNode->pChild[0] = pLeft->pChild[pLeft->keyNum]; } ++pChildNode->keyNum; pNode->keyValue[i] = pLeft->keyValue[pLeft->keyNum-1];//pLeft節點中的最大關鍵字上升到pNode中 --pLeft->keyNum; } else if (pRight&&pRight->keyNum>=CHILD_MIN)//右兄弟節點至少有CHILD_MIN個關鍵字 { //父節點中i的關鍵字下移至pChildNode中 pChildNode->keyValue[pChildNode->keyNum] = pNode->keyValue[i]; ++pChildNode->keyNum; pNode->keyValue[i] = pRight->keyValue[0];//pRight節點中的最小關鍵字上升到pNode中 --pRight->keyNum; for (j=0; j<pRight->keyNum; ++j) { pRight->keyValue[j] = pRight->keyValue[j+1]; } if (!pRight->isLeaf) { pChildNode->pChild[pChildNode->keyNum] = pRight->pChild[0];//pRight節點中合適的子女指標移植到pChildNode中 for (j=0; j<=pRight->keyNum; ++j) { pRight->pChild[j] = pRight->pChild[j+1]; } } } //左右兄弟節點都只包含CHILD_MIN-1個節點 else if (pLeft)//與左兄弟合併 { mergeChild(pNode, i-1); pChildNode = pLeft; } else if (pRight)//與右兄弟合併 { mergeChild(pNode, i); } } recursive_remove(pChildNode, key); } } T getPredecessor(Node *pNode)//找到前驅關鍵字 { while (!pNode->isLeaf) { pNode = pNode->pChild[pNode->keyNum]; } return pNode->keyValue[pNode->keyNum-1]; } T getSuccessor(Node *pNode)//找到後繼關鍵字 { while (!pNode->isLeaf) { pNode = pNode->pChild[0]; } return pNode->keyValue[0]; } private: Node * m_pRoot; //B樹的根節點 };