B树算法与实现(续)

B树实现的非回溯算法参考:http://blog.chinaunix.net/space.php?uid=20196318&do=blog&id=3030529

 

回溯的方式实现B树与非回溯算法相比,理解起来更为直观,而且插入时/删除时,需要分裂/合并的次数比非回溯算法要少,因为只有到必须分裂或合并的时候回溯算法才执行分裂或合并。当回溯算法从根向叶子下降后,还要向上回溯至根节点。如使用B树(B+树)组织辅助存储设备上(如磁盘)的数据,则应尽量避免执行节点分裂和合并操作,因为这些操作将增加多次额外的磁盘写操作,会极大的影响性能,本文主要结合代码分析B树的回溯实现方式。


数据结构定义:

  1. #define M 2
 // 这里key和pointer的最大个数都比要求的大1,主要是方便在插入时需要先插入再分裂 
  1. typedef struct btree_node {
  2.     int k[2*M];
  3.     struct btree_node *p[2*M+1]; // one more for recursive
  4.     int num;
  5.     bool is_leaf;
  6. } btree_node;

 

创建节点接口:

  1. btree_node *btree_node_new()
  2. {
  3.     btree_node *node = (btree_node *)malloc(sizeof(btree_node));
  4.     if(NULL == node) {
  5.         return NULL;
  6.     }

  7.     for(int i = 0; i < 2 * M -1; i++) {
  8.         node->k[i] = 0;
  9.     }

  10.     for(int i = 0; i < 2 * M; i++) {
  11.         node->p[i] = NULL;
  12.     }

  13.     node->num = 0;
  14.     node->is_leaf = true;
  15. }

  16. btree_node *btree_create()
  17. {
  18.     btree_node *node = btree_node_new();
  19.     if(NULL == node) {
  20.         return NULL;
  21.     }

  22.     return node;
  23. }

 

插入节点

  1. // 执行该操作时,说明child上已经有2*M个节点了,已经违反了规则
  2. // 保留左边的M-1个节点,把第M个节点上升到parent中,右边M个节点放入新节点中
  3. int btree_split_child(btree_node *parent, int pos, btree_node *child)
  4. {
  5.     btree_node *new_child = btree_node_new();
  6.     if(NULL == new_child) {
  7.         return -1;
  8.     }

  9.     new_child->is_leaf = child->is_leaf;
  10.     new_child->num = M; // M-1 left, M right
  11.     
  12.     for(int i = 0; i < M; i++) {
  13.         new_child->k[i] = child->k[i+M];
  14.     }

  15.     if(false == new_child->is_leaf) {
  16.         for(int i = 0; i < M + 1; i++) {
  17.             new_child->p[i] = child->p[i+M];
  18.         }
  19.     }

  20.     child->num = M - 1;

  21.     for(int i = parent->num; i > pos; i--) {
  22.         parent->p[i+1] = parent->p[i];
  23.     }
  24.     parent->p[pos+1] = new_child;

  25.     for(int i = parent->num - 1; i >= pos; i--) {
  26.         parent->k[i+1] = parent->k[i];
  27.     }
  28.     parent->k[pos] = child->k[M-1];
  29.     
  30.     parent->num += 1;

  31. }

  32. // 从根节点开始,一直到叶子节点,然后回溯
  33. // 回溯过程中,如果是叶子节点,则将target插入,如果是分支节点,如果子路径上有包含
  34. // 2*M个key的节点,则将其分裂(分裂时必须知道待分裂节点的父节点)
  35. void btree_insert_recursive(btree_node *node, int target)
  36. {
  37.     if(NULL != node) {
  38.         int pos = 0;
  39.         while(pos < node->num && target > node->k[pos]) pos++;
  40.         btree_insert_recursive(node->p[pos], target);
  41.         if(NULL == node->p[pos]) {
  42.             btree_insert_nonfull(node, pos, target);
  43.         } else {
  44.             if(2 * M == node->p[pos]->num) {
  45.                 btree_split_child(node, pos, node->p[pos]);
  46.             }
  47.         }
  48.     }
  49. }

 // node一定是叶子节点,在pos处插入target
  1. void btree_insert_nonfull(btree_node *node, int pos, int target)
  2. {
  3.     for(int j = node->num; j > pos; j--) {
  4.         node->k[j] = node->k[j-1];
  5.     }
  6.     node->k[pos] = target;
  7.     node->num += 1;
  8. }
 
 // 考虑特殊情况,如果最后根节点有2*M个节点,将根进行分裂,产生新的根,树增高一层
  1. btree_node* btree_insert(btree_node *root, int target)
  2. {
  3.     if(NULL == root) {
  4.         return root;
  5.     }

  6.     btree_insert_recursive(root, target);

  7.     if(2 * M == root->num) {
  8.         btree_node *node = btree_node_new();
  9.         if(NULL == node) {
  10.             return root;
  11.         }
  12.         
  13.         node->is_leaf = false;
  14.         node->p[0] = root;
  15.         btree_split_child(node, 0, root);
  16.         return node;
  17.     }

  18.     return root;
  19. }

 

删除节点

  1. // 执行该操作时,一定是y或者z的key个数少于M-1,将y,root->k[pos],z合并到y,释放z
  2. void btree_merge_child(btree_node *root, int pos, btree_node *y, btree_node *z)
  3. {
  4.     int n = y->num;
  5.     for(int i = 0; i < z->num; i++) {
  6.         y->k[n+1+i] = z->k[i];
  7.     }
  8.     y->k[n] = root->k[pos];
  9.     
  10.     if(false == z->is_leaf) {
  11.         for(int i = 0; i <= z->num; i++) {
  12.             y->p[n+1+i] = z->p[i];
  13.         }
  14.     }

  15.     y->num += (z->num + 1);

  16.     for(int j = pos + 1; j < root->num; j++) {
  17.         root->k[j-1] = root->k[j];
  18.         root->p[j] = root->p[j+1];
  19.     }

  20.     root->num -= 1;
  21.     free(z);
  22. }

 // 递归删除target,在回溯的过程中,如果遇到叶子节点,则将target删除
 // 如果遇到分支节点,则检查子树的节点数是否少于M-1,如果是,则需要从左或
 // 右兄弟借节点,如果左右兄弟都只有M-1个节点,则要执行merge
  1. void btree_delete_recursive(btree_node *node, int target)
  2. {
  3.     if(NULL != node) {
  4.         int i = 0;
  5.         while(i < node->num && target > node->k[i]) i++;

  6.         if(i < node->num && target == node->k[i] && false == node->is_leaf) { // found
  7.             btree_node *y = node->p[i];
  8.             int pre = btree_search_predecessor(y);
  9.             node->k[i] = pre;
  10.             target = pre;
  11.         }
  12.         btree_delete_recursive(node->p[i], target);

  13.         if(NULL == node->p[i]) {
  14.             btree_delete_nonone(node, i, target);
  15.         } else {
  16.             btree_node *y = node->p[i];
  17.             if(M - 2 == y->num) {
  18.                 btree_node *p = NULL, *z = NULL;
  19.                 if(i > 0) {
  20.                     p = node->p[i-1];
  21.                 }    
  22.                 if(i < node->num) {
  23.                     z = node->p[i+1];
  24.                 }

  25.                 if(i > 0 && p->num > M - 1) {
  26.                     btree_shift_to_right_child(node, i-1, p, y);
  27.                 } else if(i < node->num && z->num > M - 1) {
  28.                     btree_shift_to_left_child(node, i, y, z);
  29.                 } else if(i > 0) {
  30.                     btree_merge_child(node, i-1, p, y); // note
  31.                     y = p;
  32.                 } else {
  33.                     btree_merge_child(node, i, y, z);
  34.                 }
  35.             }
  36.         }
  37.     }
  38. }

 // 考虑特殊情况,删除完后,根节点无任何key了,这时释放根,将子树作为新的根
  1. btree_node *btree_delete(btree_node *root, int target)
  2. {
  3.     if(NULL == root) {
  4.         return NULL;
  5.     }

  6.     btree_delete_recursive(root, target);
  7.     
  8.     if(== root->num && false == root->is_leaf) {
  9.         btree_node *newroot = root->p[0];
  10.         free(root);
  11.         return newroot;
  12.     } 

  13.     return root;
  14. }
  1. // 如果node的第pos个key为target,则删除之,否则说明树中没有待删除的节点
  2. void btree_delete_nonone(btree_node *node, int pos, int target)
  3. {
  4.     if(node->k[pos] != target) {
  5.         printf("target not found\n");
  6.     }
  7.     else {
  8.         for(int j = pos; j < node->num - 1; j++) {
  9.             node->k[j] = node->k[j+1];
  10.         }
  11.         
  12.         (void)target;
  13.         node->num -= 1;
  14.     }
  15. }

 // find rightmost key
  1. int btree_search_predecessor(btree_node *root)
  2. {
  3.     btree_node *y = root;
  4.     while(false == y->is_leaf) {
  5.         y = y->p[y->num];
  6.     }
  7.     return y->k[y->num-1];
  8. }

 // find leftmost key 
  1. int btree_search_successor(btree_node *root)
  2. {
  3.     btree_node *z = root;
  4.     while(false == z->is_leaf) {
  5.         z = z->p[0];
  6.     }
  7.     return z->k[0];
  8. }

  // 从左兄弟借一个节点,y的最大key上升至root,root的k[pos]下降至z
  1. void btree_shift_to_right_child(btree_node *root, int pos,
  2.         btree_node *y, btree_node *z)
  3. {

  4.     for(int i = z->num -1; i > 0; i--) {
  5.         z->k[i] = z->k[i-1];
  6.     }
  7.     z->k[0]= root->k[pos];
  8.     root->k[pos] = y->k[y->num-1];

  9.     if(false == z->is_leaf) {
  10.         for(int i = z->num; i > 0; i--) {
  11.             z->p[i] = z->p[i-1];
  12.         }
  13.         z->p[0] = y->p[y->num];
  14.     }

  15.     y->num -= 1;
  16. }

  // 从右兄弟借一个节点,z的最小key上升至root,root的看k[pos]下降至y 
  1. void btree_shift_to_left_child(btree_node *root, int pos,
  2.         btree_node *y, btree_node *z)
  3. {
  4.     y->num += 1;
  5.     y->k[y->num-1] = root->k[pos];
  6.     root->k[pos] = z->k[0];

  7.     for(int j = 1; j < z->num; j++) {
  8.         z->k[j-1] = z->k[j];
  9.     }

  10.     if(false == z->is_leaf) {
  11.         y->p[y->num] = z->p[0];
  12.         for(int j = 1; j <= z->num; j++) {
  13.             z->p[j-1] = z->p[j];
  14.         }
  15.     }

  16.     z->num -= 1;
  17. }
 


 btree_rec.rar   g++ -o btree_rec btree_rec.c

posted @ 2013-04-19 14:12  ydzhang  阅读(235)  评论(0编辑  收藏  举报