《演算法導論》中BTree的程式碼實現~
阿新 • • 發佈:2019-02-14
BTree.h
#ifndef _BTREE_H #define _BTREE_H #define MINDEGREE 3 // 定義BTree的最小度 #define MAXDEGREE (MINDEGREE*2) // 定義BTree的資料結構 typedef void* NodeData; typedef struct _targBTreeNode { NodeData data; // _targBTreeNode* cs[MAXDEGREE]; //孩子指標陣列 int keys[MAXDEGREE-1]; //關鍵字陣列 int count; // 關鍵字個數 int is_leaf; // 是否為葉子結點 } BTreeNode, *BTree; #define BTREE_SIZE sizeof(BTreeNode) void alloc_tree(BTree &tree); //分配記憶體 void insert_keys_to_tree(BTree &tree, int keys[], int n); //將keys插入至tree中 void insert_key_to_tree(BTree &tree, int key); // 將key插入至根樹tree中 void insert_key_to_unfull_tree(BTree &tree, int key); // 將key關鍵字插入至未滿子樹tree中 void break_tree_child(BTree &tree, BTree &child, int i); // 將tree的第i個子孩子分裂 void disk_read(const BTree tree); // 代表讀磁碟,未實現 void disk_write(const BTree tree); // 代表寫磁碟,未實現 void display_tree(const BTree tree); // 顯示BTree int search_tree(const BTree tree, int key); // 搜尋BTree中是否存在關鍵字 void delete_tree(BTree &tree, int key); // 刪除BTree中的某一關鍵字,主要處理BTree為空的兩種情況~ void delete_unless_tree(BTree &tree, int key); // 刪除BTree中的某一子樹~ int pre_succor_tree(const BTree tree, int i); // 找tree中第i位關鍵字的前驅~ int after_succor_tree(const BTree tree, int i); // 找Btree中第i位關鍵字的後繼~ #endif
BTree.cpp
#include "BTree.h" #include <stdio.h> #include <stdlib.h> #include <memory.h> #include <assert.h> void alloc_tree(BTree &tree) { if(0 == (tree = (BTree) malloc (BTREE_SIZE))) { exit(-1); } memset(tree, 0, BTREE_SIZE); tree->is_leaf = 1; } void insert_keys_to_tree(BTree &tree, int keys[], int n) { for(int i = 0; i < n; i++) { insert_key_to_tree(tree, keys[i]); } } void insert_key_to_tree(BTree &tree, int key) { if(tree == NULL) { alloc_tree(tree); } // 如果根關鍵字已滿,則進行分裂 if(tree->count == (MAXDEGREE-1)) { BTree new_root = NULL; alloc_tree(new_root); new_root->is_leaf = 0; new_root->cs[0] = tree; break_tree_child(new_root, tree, 0); // 分裂 tree = new_root; insert_key_to_unfull_tree(tree, key); } else { insert_key_to_unfull_tree(tree, key); } } void break_tree_child(BTree &tree, BTree &child, int i) { int j = 0; BTree rchild = NULL; alloc_tree(rchild); rchild->is_leaf = child->is_leaf; // 和原child一致 // 將原child分成兩個孩子 // 將原child的右側r-1個關鍵字移至rchild的keys中 for(j = 0; j < (MINDEGREE-1); j++) { rchild->keys[j] = child->keys[MINDEGREE+j]; } rchild->count = MINDEGREE - 1; // 將原child的右側的r+1個孩子移至rchild的cs中 for(j = 0; j < MINDEGREE; j++) { rchild->cs[j] = child->cs[MINDEGREE+j]; } child->count = MINDEGREE - 1; //將child的第r個結點關鍵字插入tree的第i個位置 // 將tree從第i+1至第(tree->count)位置的cs後移一位 for(j=tree->count;j > i; j--) { tree->cs[j+1] = tree->cs[j]; } tree->cs[i+1] = rchild; // 將從第i至(tree->count-1)位置的關鍵字全部向後挪1 for(j=tree->count-1; j > i; j--) { tree->keys[j+1] = tree->keys[j]; } tree->keys[i] = child->keys[MINDEGREE-1]; tree->count++; disk_write(tree); disk_write(child); disk_write(rchild); } void insert_key_to_unfull_tree(BTree &tree, int key) { int i = 0; // 如果是葉子結點,則直接插入即可,由於tree未全滿,則符合BTree定義 if(tree->is_leaf == 1) { // 找到需要沿下插入的位置 i=tree->count-1; while(i>=0 && key < tree->keys[i]) { tree->keys[i+1] = tree->keys[i]; i--; } tree->keys[i+1] = key; tree->count++; disk_write(tree); } else { // 如果是內結點,則需找到繼續尋找位置後,判斷其子結點是否已經全滿,如全滿則分裂,並繼續沿下尋找 i=tree->count-1; while(i>=0 && key < tree->keys[i]) { i--; } i++; // 找到繼續尋找位置 disk_read(tree->cs[i]); // 如果子樹已滿,則將其分裂 if(tree->cs[i]->count == (MAXDEGREE-1)) { break_tree_child(tree, tree->cs[i], i); if(key > tree->keys[i]) i++; } insert_key_to_unfull_tree(tree->cs[i], key); } } void disk_read(const BTree tree) { } void disk_write(const BTree tree) { } void display_tree(const BTree tree) { if(tree==NULL || tree->count == 0) { return; } printf("(%d", tree->keys[0]); int i = 0; for(i = 1; i < tree->count; i++) { printf(",%d", tree->keys[i]); } printf(")"); if(0 == tree->is_leaf) { printf("("); for(i = 0; i < tree->count+1; i++) { display_tree(tree->cs[i]); } printf(")"); } } int search_tree(const BTree tree, int key) { if(tree==NULL) return 0; for(int i = tree->count-1; (i >= 0) && (key < tree->keys[i]); i--); // 在遍歷結束之前找到匹配關鍵字 if((i != -1) && (key == tree->keys[i])) { return 1; } i++; if(1 == tree->is_leaf) { return 0; } else { return search_tree(tree->cs[i], key); } } void delete_tree(BTree &tree, int key) { if(0 == search_tree(tree, key)) { return; } // 查詢關鍵字位置,或子樹 if( (tree->count==1) && (1 == tree->is_leaf)) { free(tree); tree = NULL; return; } else if((tree->count==1) && (tree->keys[0] == key) && (tree->cs[0]->count == MINDEGREE-1) && (tree->cs[1]->count == MINDEGREE-1)) { int j = 0; BTree l_child = tree->cs[0], r_child = tree->cs[1]; l_child->keys[MINDEGREE-1] = tree->keys[0]; for(j = 0; j < r_child->count; j++) { l_child->keys[j+MINDEGREE] = r_child->keys[j]; } for(j = 0; j < MINDEGREE; j++) { l_child->cs[MINDEGREE+j] = r_child->cs[j]; } l_child->count = MAXDEGREE - 1; free(r_child); free(tree); tree = l_child; delete_unless_tree(tree, key); } else { delete_unless_tree(tree, key); } } void delete_unless_tree(BTree &tree, int key) { // 查詢關鍵字位置,或子樹 int i = 0, j = 0; for(i = tree->count-1; (i>=0) && (key < tree->keys[i]); i--); // 由於已經確保關鍵字在此樹中,且無子樹,故直接刪除 if(1 == tree->is_leaf) { // 即將從第i+1位置至(tree->count-1)位置關鍵字都往前挪1位,並tree->count-- for(j = i + 1; j < (tree->count); j++) { tree->keys[j-1] = tree->keys[j]; } tree->count--; } else if((i != -1) && (tree->keys[i] == key)) {// 如果關鍵字在此樹當中,但為內結點,~ // case2a 若其左孩子有大於等於r個結點,則將其前繼代替tree->key[i],並移至左孩子,將其遞迴刪除~ if(tree->cs[i]->count >= MINDEGREE) { int new_key = pre_succor_tree(tree, i); tree->keys[i] = new_key; delete_unless_tree(tree->cs[i], new_key); } else if(tree->cs[i+1]->count >= MINDEGREE) {//case2b 若右孩子大於等於r個結點,則將其後繼代替tree->key[i], 並移至右孩子,遞迴刪除~ int new_key = after_succor_tree(tree, i+1); delete_unless_tree(tree->cs[i+1], new_key); } else {//case2c 若左右孩子均為r-1個關鍵字,則需將刪除結點下沉並左右孩子合併,再刪除~ BTree l_child = tree->cs[i], r_child = tree->cs[i+1]; l_child->keys[MINDEGREE-1] = tree->keys[i]; for(j = 0; j < r_child->count; j++) { l_child->keys[j+MINDEGREE] = r_child->keys[j]; } for(j = 0; j < MINDEGREE; j++) { l_child->cs[MINDEGREE+j] = r_child->cs[j]; } l_child->count = MAXDEGREE - 1; free(r_child); // 將keys域中(i+1)位至(tree->count-1)位向前移1位 for(j = i+1; j < (tree->count -1); j++) { tree->keys[j-1] = tree->keys[j]; } // 將cs域中從(i+2)位至(tree->count)位向前移1位 for(j = i+2; j < tree->count; j++) { tree->cs[j-1] = tree->cs[j]; } tree->count--; delete_unless_tree(tree->cs[i], key); } } else { // 如果不在tree的keys中,且為內結點~ i++; // 找至子孩子的正確位置 if(tree->cs[i]->count >= MINDEGREE) {// 如果關鍵字個數>=最小度 delete_unless_tree(tree->cs[i], key); } else {// 如果子樹根只有MINDEGREE-1個關鍵字,則需要考慮從兄弟借,或與兄弟合併情況 // case 3a 如果可以從其左孩子借一關鍵字 if((i != 0) &&(tree->cs[i-1]->count >= MINDEGREE)) { BTree ichild = tree->cs[i], lchild = tree->cs[i-1]; // 將i孩子的所有子孩子右移1,以便為lchild的最後一個子孩子移至第0位置 for(j = tree->count; j >= 0; j--) { ichild->cs[j+1] = ichild->cs[j]; } ichild->cs[0] = lchild->cs[lchild->count]; // 將i孩子的所有關鍵字右移1,以便為tree中第(i-1)位置的key移至第1個位置 for(j = tree->count-1; j >= 0; j--) { ichild->keys[j+1] = ichild->keys[j]; } ichild->keys[0] = tree->keys[i-1]; ichild->count++; // 將tree中第(i-1)位關鍵字替換成lchild中最右關鍵字 tree->keys[i-1] = lchild->keys[lchild->count-1]; // 將lchild中左孩子左移1 for(j = 0; j < lchild->count; j++) { lchild->cs[j] = lchild->cs[j+1]; } // 將lchild中左孩子關鍵字左移1 for(j = 0; j < (lchild->count-1); j++) { lchild->keys[j] = lchild->keys[j+1]; } lchild->count--; delete_unless_tree(ichild, key); }// if case 3a else if((i != tree->count) && tree->cs[i+1]->count >= MINDEGREE) {// case3a' 如果child i的右兄弟關鍵字夠“借”(即>=MINDEGREE) BTree ichild = tree->cs[i], rchild = tree->cs[i+1]; // 將rchild的第0個子孩子移至ichild中子孩子最未位置中 ichild->cs[ichild->count+1] = rchild->cs[0]; // 將tree的第i個關鍵字加至ichild的關鍵字中 ichild->keys[ichild->count] = tree->keys[i]; ichild->count++; // 將rchild的第0個關鍵字覆蓋至tree中 tree->keys[i] = rchild->keys[0]; // 將rchild的所有子孩子左移1 for(j = 0; j < rchild->count; j++) { rchild->cs[j] = rchild->cs[j+1]; } for(j = 0; j < (rchild->count-1); j++) { rchild->keys[j] = rchild->keys[j+1]; } rchild->count--; delete_unless_tree(ichild, key); }// else if else {// case3b 左右兄弟都為MINDEGREE-1的情況 // 如沒有左兄弟,則將i+1,轉至有左兄弟的情況進行處理 if(i == 0) { i++; } BTree ichild = tree->cs[i]; BTree lchild = tree->cs[i-1]; // 將i的子孩子移至左兄弟 for(j = 0; j < MINDEGREE; j++) { lchild->cs[j+MINDEGREE] = ichild->cs[j]; } // 將tree的關鍵字i-1移至左兄弟中 lchild->keys[MINDEGREE-1] = tree->keys[i-1]; // 將i的關鍵字都移至左兄弟中 for(j = 0; j < MINDEGREE-1; j++) { lchild->keys[MINDEGREE+j] = ichild->keys[j]; } lchild->count = MAXDEGREE-1; free(ichild); // 調整tree中的子孩子位置 for(j=i; j < tree->count; j++) { tree->cs[j] = tree->cs[j+1]; } // 調整tree中關鍵字位置 for(j=i-1; j < (tree->count-1); j++) { tree->keys[j] = tree->keys[j+1]; } tree->count--; delete_unless_tree(lchild, key); } }// else }// else } int pre_succor_tree(const BTree tree, int i) { BTree p = tree->cs[i]; //前驅為tree的最左子樹的最右葉子結點的最後右一個關鍵字 while((p->is_leaf == 0)) { p = p->cs[p->count]; } return p->keys[p->count-1]; } int after_succor_tree(const BTree tree, int i) { BTree p = tree->cs[i+1]; //後繼為tree的右子樹最左葉子結點的第一個關鍵字 while(p->is_leaf == 0) { p = p->cs[0]; } return p->keys[0]; } //////////////////////////////////// // 定義測試資料 #define TEST_NUM 11 int test_keys[TEST_NUM] = {10, 2, 1, 5, 4, 7, 8, 6, 13, 11, 14}; int main() { BTree tree = NULL; insert_keys_to_tree(tree, test_keys, TEST_NUM); display_tree(tree); delete_tree(tree, 1); printf("\n\nafter delete 1:\n"); display_tree(tree); delete_tree(tree, 7); printf("\n\nafter delete 7:\n"); display_tree(tree); delete_tree(tree, 4); printf("\n\nafter delete 4:\n"); display_tree(tree); delete_tree(tree, 2); printf("\n\nafter delete 2:\n"); display_tree(tree); printf("\n"); return 0; }
執行截圖