B-Tree

Index



Basic


B-Tree有两个比较重要的性质:

  • 所有的leaf均在同一个level上
  • 除了root之外,其它所有node中所储存的数据至少为Minimum Degree - 1,至多为Minimum Degree * 2 - 1

两个结构定义,其中struct TreeNode定义了B-Tree中的一个node,而struct Tree则是包含了一个指向B-Tree的root的指针,相当于一个dummy node,设置这个结构的目的是为了可以将其作为参数传入函数中,以便可以在函数中修改root节点的值。

  1. typedef struct TreeNode *PtrBTNode;
  2. typedef struct TreeNode BTNode;
  3. struct TreeNode{
  4. int Num;
  5. Bool IsLeaf;
  6. PtrElementType Key;
  7. PtrBTNode *Child;
  8. };
  9. typedef struct Tree *PtrBT;
  10. struct Tree{
  11. PtrBTNode Root;
  12. };

其他的一些定义和声明,其中ShiftKey函数用于对每个node中的Key数组进行部分左移或右移,Direction控制左移或右移方向(1-右移,0-左移),而BeginEnd参数则是用来传递被移动部分的最左端和最右端的元素下标,ShiftChild函数同理针对Child数组;GetIndex函数则是用来搜索在一个节点中大于或等于Val的最小元素的下标,如果均小于Val,就返回数组的大小(就是刚好越界了):

  1. #define MinDegree 3
  2. typedef int ElementType;
  3. typedef int* PtrElementType;
  4. typedef enum BoolType Bool;
  5. enum BoolType{
  6. False = 0,
  7. True = 1
  8. };
  9. void ShiftKey(PtrElementType Key, Bool Direction, int Begin, int End){
  10. int i;
  11. if(True == Direction){
  12. for(i = End; i >= Begin; i--){
  13. Key[i + 1] = Key[i];
  14. }
  15. }
  16. else{
  17. for(i = Begin; i <= End; i++){
  18. Key[i - 1] = Key[i];
  19. }
  20. }
  21. }
  22. void ShiftChild(PtrBTNode *Child, Bool Direction, int Begin, int End){
  23. int i;
  24. if(True == Direction){
  25. for(i = End; i >= Begin; i--){
  26. Child[i + 1] = Child[i];
  27. }
  28. }
  29. else{
  30. for(i = Begin; i <= End; i++){
  31. Child[i - 1] = Child[i];
  32. }
  33. }
  34. }
  35. int GetIndex(PtrElementType Key, int Size, ElementType Val){
  36. int i;
  37. for(i = 0; i < Size; i++){
  38. if(Key[i] >= Val){
  39. break;
  40. }
  41. }
  42. return i;
  43. }
  44. void BTPrintTree(PtrBTNode Root){
  45. int i;
  46. if(NULL == Root){
  47. return;
  48. }
  49. putchar('[');
  50. for(i = 0; i < Root->Num; i++){
  51. printf("%d", Root->Key[i]);
  52. if(i != Root->Num - 1){
  53. putchar(' ');
  54. }
  55. }
  56. putchar(']');
  57. printf("%d", Root->IsLeaf);
  58. putchar('\n');
  59. for(i = 0; i <= Root->Num; i++){
  60. BTPrintTree(Root->Child[i]);
  61. }
  62. }
  63. void BTCreateTree(PtrBT T){
  64. int i;
  65. //A test case
  66. int a[] = {12,1,9,2,0,11,7,19,4,15,18,5,14,13,10,16,6,3,8,17,20,21,23};
  67. for(i = 0; i < 23; i++){
  68. BTInsert(T, a[i]);
  69. BTPrintTree(T->Root);
  70. printf("The End\n");
  71. }
  72. }

Search


搜索节点的过程比较简单,下面是函数代码:

  1. PtrBTNode BTSearch(PtrBTNode Root, ElementType Val, int* Index){
  2. int i;
  3. for(i = 0; i < Root->Num&&Val > Root->Key[i]; i++){
  4. ;
  5. }
  6. if(i < Root->Num&&Root->Key[i] == Val){
  7. *Index = i;
  8. return Root;
  9. }
  10. else if(True == Root->IsLeaf){
  11. return NULL;
  12. }
  13. else{
  14. return BTSearch(Root->Child[i], Val, Index);
  15. }
  16. }

Insert


在B-Tree中插入一个值,为了不破坏B-Tree的第一个性质,必须将其插入在leaf中,但是又因为B-Tree同时具有第二个性质,所以要保证待插入值一定能成功插入,必须使得所有节点都是非满的,因此在某一个节点成为满节点时,就必须将该节点分裂为两个节点(分裂节点:将满节点分裂为两个包含Minimum Degree - 1个数据的节点,并将原来满节点中数据的中值插入原满节点的父节点中,同时在原满节点的父节点中插入指向两个分裂所得节点的指针,当然实际上只需要插入一个新增指针)。虽然只要leaf保持非满就一定能成功插入,但是当leaf成为满节点分裂时,会有一个数据向上插入到其父节点中,所以必须保证其父节点也非满,如果父节点被插入后成为满节点还需要继续分裂并向上插入数据,依此类推,相当于所有节点都需要保持非满状态。

在插入时向上调整的过程中看似需要递归回溯,其实可以在向下搜索插入位置时,对于即将搜索的下一个节点,都检查其是否为满节点,如果是满节点就将其分裂。而又因为对于搜索路径上的每个节点都进行判断,所以搜索路径上已经搜索过的节点一定是非满的,那么即将搜索到的节点如果需要分裂,分裂后向上插入的那个数据就一定能够插入成功。综上所述,这样的处理方式一定能够成功执行,并且可以保证数据一定能够插入成功。

分裂节点

这个函数传入的是指向待分裂节点的父节点的指针和指向待分裂节点的指针在其父节点Child数组中的下标。

首先用BTAllocateNode函数新建一个节点NewNode,将待分裂节点的Minimum Degree - 1个数据和Minimum Degree个Child指针转移到NewNode中,同时应当注意NewNode和待分裂节点应该处于同一层,即两者Isleaf成员的值应当保持一致(即下面代码段中的Caution注释行),再将待分裂节点的父节点中Key数组和Child数组的部分元素向右移动,为之后的插入腾出位置,最后将待分裂节点中数据的中值插入其父节点中,并将指向NewNode的指针也插入待分裂节点的父节点中。

  1. void BTChildSplit(PtrBTNode SplitNodeP, int ChildIndex){
  2. int i;
  3. PtrBTNode NewNode = BTAllocateNode();
  4. PtrBTNode FullNode = SplitNodeP->Child[ChildIndex];
  5. for(i = 0; i < MinDegree - 1; i++){
  6. NewNode->Key[i] = FullNode->Key[MinDegree + i];
  7. }
  8. if(False == FullNode->IsLeaf){
  9. NewNode->IsLeaf = False;//Caution!
  10. for(i = 0; i < MinDegree; i++){
  11. NewNode->Child[i] = FullNode->Child[MinDegree + i];
  12. }
  13. }
  14. NewNode->Num = FullNode->Num = MinDegree - 1;
  15. ShiftKey(SplitNodeP->Key, 1, ChildIndex, SplitNodeP->Num - 1);
  16. SplitNodeP->Key[ChildIndex] = FullNode->Key[MinDegree - 1];
  17. ShiftChild(SplitNodeP->Child, 1, ChildIndex + 1, SplitNodeP->Num);
  18. SplitNodeP->Child[ChildIndex + 1] = NewNode;
  19. (SplitNodeP->Num)++;
  20. }
  21. PtrBTNode BTAllocateNode(){
  22. int i;
  23. PtrBTNode NewNode = (PtrBTNode)malloc(sizeof(BTNode));
  24. NewNode->Num = 0;
  25. NewNode->IsLeaf = True;
  26. NewNode->Key = (PtrElementType)malloc(sizeof(ElementType) * (MinDegree * 2 - 1));
  27. NewNode->Child = (PtrBTNode*)malloc(sizeof(PtrBTNode) * (MinDegree * 2));
  28. for(i = 0; i < MinDegree * 2; i++){
  29. NewNode->Child[i] = NULL;
  30. }
  31. return NewNode;
  32. }

插入节点

BTInsertNonFull函数首先找到合适的插入位置,如果当前节点是leaf则直接插入,如果不是则继续向下搜索相应位置的子树(即包含最终有数据插入的leaf的子树),当然在搜索之前需要判断即将搜索的节点是否为满节点,为满节点则将其分裂,因为分裂后原来的满节点会向上插入一个数据,所以就需要重新判断新的向下搜索位置(即下面代码段中Caution注释行)

BTInsert则是因为BTInsertNonFull函数并不能处理插入时root为满节点的情况,所以需要这个函数来判断,当root为满节点时新建一个root节点,再将原root节点分裂,这里有两个需要注意的地方,第一是在分裂开始前新建的root节点的Isleaf成员值需要改为False,并且新建的root节点的Num成员值虽然还是0,但Child数组的首元素应当指向原root节点,第二是结构T中的root指针需要更改。

  1. void BTInsertNonFull(PtrBTNode CurrentNode, ElementType Val){
  2. int Index = GetIndex(CurrentNode->Key, CurrentNode->Num, Val);
  3. if(True == CurrentNode->IsLeaf){
  4. ShiftKey(CurrentNode->Key, 1, Index, CurrentNode->Num - 1);
  5. CurrentNode->Key[Index] = Val;
  6. (CurrentNode->Num)++;
  7. }
  8. else{
  9. if(MinDegree * 2 - 1 == CurrentNode->Child[Index]->Num){
  10. BTChildSplit(CurrentNode, Index);
  11. if(CurrentNode->Key[Index] < Val){//Caution
  12. Index++;
  13. }
  14. }
  15. BTInsertNonFull(CurrentNode->Child[Index], Val);
  16. }
  17. }
  18. void BTInsert(PtrBT T, ElementType Val){
  19. PtrBTNode NewNode;
  20. if(MinDegree * 2 - 1 == T->Root->Num){
  21. NewNode = BTAllocateNode();
  22. NewNode->IsLeaf = False;
  23. NewNode->Child[0] = T->Root;
  24. T->Root = NewNode;
  25. BTChildSplit(NewNode, 0);
  26. }
  27. BTInsertNonFull(T->Root, Val);
  28. }

Delete


删除操作稍微有些复杂,因为B-Tree具有的第二个性质,所以必须保证一个节点在删除一个数据之后数据量不小于Minimum Degree - 1,因此类似于插入的过程,在删除时,对于即将搜索到的下一个节点,如果在搜索之前该节点的数据量已经为Minimum Degree - 1,则考虑从兄弟节点“借”一个数据填充,若其兄弟节点也只剩Minimum Degree - 1个数据,则考虑和兄弟节点合并;对于需要有数据被删除的节点,如果是叶节点则直接删除即可,如果不是叶节点,因为单纯的删除数据会使得Child数组的Size比Key数组的大2,不符合B-Tree的要求,因此考虑从前驱或者后继节点找一个数据填充到被删除数据的位置上,然后再递归地删除前驱或者后继节点中被用来填充的数据,如果前驱或后继节点的数据量均只剩Minimum Degree - 1,则考虑合并前驱和后继节点,然后在新的节点中递归删除(具体的在后面)。

合并节点

函数传入的参数是待合并节点的父节点和指向两个待合并节点的指针在其父节点Child数组中的下标,函数先将右边待合并节点中的KeyChild数组依次赋值给左边待合并节点,当然左边待合并节点的Key数组的中点位置需要空出,因为合并后待合并节点的父节点的Child数组少了一个元素,相应的Key数组也应该减少一个元素,因此将两个待合并节点在其父节点中所夹的数据移动到左边待合并节点的Key数组中点位置,然后将待合并节点的父节点的KeyChild数组中的部分元素向左移动,合并完成。不过这里需要注意的是如果是root的子节点需要合并并且合并后root中无数据,这样的情况下就应该更改结构Troot指针的值(即下面程序中Caution注释行)

  1. void Merge(PtrBT T, PtrBTNode ParentNode, int LeftIndex, int RightIndex){
  2. PtrBTNode LeftNode = ParentNode->Child[LeftIndex], RightNode = ParentNode->Child[RightIndex];
  3. int i;
  4. for(i = 0; i < MinDegree - 1; i++){
  5. LeftNode->Key[MinDegree + i] = RightNode->Key[i];
  6. }
  7. if(False == LeftNode->IsLeaf){
  8. for(i = 0; i < MinDegree; i++){
  9. LeftNode->Child[MinDegree + i] = RightNode->Child[i];
  10. }
  11. }
  12. LeftNode->Key[MinDegree - 1] = ParentNode->Key[LeftIndex];
  13. LeftNode->Num = MinDegree * 2 - 1;
  14. ShiftKey(ParentNode->Key, 0, LeftIndex + 1, ParentNode->Num - 1);
  15. ShiftChild(ParentNode->Child, 0, RightIndex + 1, ParentNode->Num);
  16. (ParentNode->Num)--;
  17. if(ParentNode == T->Root && 0 == ParentNode->Num){//Caution
  18. T->Root = LeftNode;
  19. }
  20. }

删除节点

删除节点有很多情况:

  1. 待删除数据Val在当前节点中

    1.1 如果当前节点是leaf则直接删除

    1.2 如果当前节点不是leaf则检查夹着Val的两个子节点

    • 如果左子节点的数据数量大于Minimum Degree - 1,则用左子节点中最大的数据来代替Val,递归地删除左子节点的最大数据

    • 如果右子节点的数据数量大于Minimum Degree - 1,则用右子节点中最小的数据来代替Val,递归地删除右子节点的最小数据

    • 如果以上两种情况均不符合则合并Val、左子节点和右子节点,在合并后的新节点中递归地删除Val

  2. 待删除数据Val不在当前节点中

    2.1 如果当前节点是leaf则表示B-Tree中没有该数据

    2.2 如果当前节点不是叶节点,当前节点中大于Val的数据中最小值的下标为Index,假设当前节点的Child[Index]所指节点为SubNode,其左兄弟为LeftNode,右兄弟为RightNode,需要注意这里的Index可能等于当前节点的数据量大小

    • 如果SubNode中的数据量大于Minimum Degree - 1,则直接在SubNode中递归删除Val

    • 如果SubNode中的数据量小于等于Minimum Degree - 1(小于时为root),则(以下情况均需要考虑LeftNode和RightNode是否存在(即下面代码中的Caution注释行)

      • 如果LeftNode中的数据量大于Minimum Degree - 1,将Val左边的数据插入SubNode中,再用LeftNode中最大的数据来填入Val左边的数据的空缺中,并将LeftNode中最右端的子节点移动到SubNode中,然后在SubNode中递归删除Val

      • 如果RightNode中的数据量大于Minimum Degree - 1,将Val插入SubNode中,再用RightNode中最小的数据来填入Val的空缺中,并将RightNode中最左端的子节点移动到SubNode中,然后在SubNode中递归删除Val

      • 如果以上两种情况均不符合,则将LeftNode或RightNode和SubNode合并,然后在新的节点中递归的删除Val

  1. void BTDelete(PtrBT T, PtrBTNode CurrentNode, ElementType Val){
  2. int Index;
  3. PtrBTNode Precursor, Successor, SubNode;
  4. Index = GetIndex(CurrentNode->Key, CurrentNode->Num, Val);
  5. SubNode = CurrentNode->Child[Index];
  6. if(Index < CurrentNode->Num && CurrentNode->Key[Index] == Val){
  7. if(True == CurrentNode->IsLeaf){
  8. ShiftKey(CurrentNode->Key, 0, Index + 1, CurrentNode->Num - 1);
  9. (CurrentNode->Num)--;
  10. return;
  11. }
  12. else{
  13. Precursor = CurrentNode->Child[Index];
  14. Successor = CurrentNode->Child[Index + 1];
  15. if(Precursor->Num > MinDegree - 1){
  16. CurrentNode->Key[Index] = Precursor->Key[Precursor->Num - 1];
  17. BTDelete(T, Precursor, Precursor->Key[Precursor->Num - 1]);
  18. }
  19. else if(Successor->Num > MinDegree - 1){
  20. CurrentNode->Key[Index] = Successor->Key[0];
  21. BTDelete(T, Successor, Successor->Key[0]);
  22. }
  23. else{
  24. Merge(T, CurrentNode, Index, Index + 1);
  25. BTDelete(T, CurrentNode->Child[Index], Val);
  26. }
  27. }
  28. }
  29. else{
  30. if(True == CurrentNode->IsLeaf){
  31. return;
  32. }
  33. else{
  34. if(SubNode->Num > MinDegree - 1){
  35. BTDelete(T, SubNode, Val);
  36. }
  37. else{
  38. //Caution
  39. if(Index > 0){
  40. Precursor = CurrentNode->Child[Index - 1];
  41. }
  42. if(Index < CurrentNode->Num){
  43. Successor = CurrentNode->Child[Index + 1];
  44. }
  45. if(Index > 0 && Precursor->Num > MinDegree - 1){
  46. ShiftKey(SubNode->Key, 1, 0, SubNode->Num - 1);
  47. ShiftChild(SubNode->Child, 1, 0, SubNode->Num);
  48. SubNode->Key[0] = CurrentNode->Key[Index - 1];
  49. SubNode->Child[0] = Precursor->Child[Precursor->Num];
  50. (SubNode->Num)++;
  51. CurrentNode->Key[Index - 1] = Precursor->Key[Precursor->Num - 1];
  52. (Precursor->Num)--;
  53. BTDelete(T, SubNode, Val);
  54. }
  55. else if(Index < CurrentNode->Num && Successor->Num > MinDegree - 1){
  56. SubNode->Key[SubNode->Num] = CurrentNode->Key[Index];
  57. SubNode->Child[SubNode->Num + 1] = Successor->Child[0];
  58. (SubNode->Num)++;
  59. CurrentNode->Key[Index] = Successor->Key[0];
  60. ShiftKey(Successor->Key, 0, 1, Successor->Num - 1);
  61. ShiftChild(Successor->Child, 0, 1, Successor->Num);
  62. (Successor->Num)--;
  63. BTDelete(T, CurrentNode->Child[Index], Val);
  64. }
  65. else{
  66. if(Index > 0){
  67. Merge(T, CurrentNode, Index - 1, Index);
  68. BTDelete(T, Precursor, Val);
  69. }
  70. else{
  71. Merge(T, CurrentNode, Index, Index + 1);
  72. BTDelete(T, SubNode, Val);
  73. }
  74. }
  75. }
  76. }
  77. }
  78. }

Source Code With A Simple Test Case


  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #define MinDegree 3
  4. typedef int ElementType;
  5. typedef int* PtrElementType;
  6. typedef enum BoolType Bool;
  7. enum BoolType{
  8. False = 0,
  9. True = 1
  10. };
  11. typedef struct TreeNode *PtrBTNode;
  12. typedef struct TreeNode BTNode;
  13. struct TreeNode{
  14. int Num;
  15. Bool IsLeaf;
  16. PtrElementType Key;
  17. PtrBTNode *Child;
  18. };
  19. typedef struct Tree *PtrBT;
  20. struct Tree{
  21. PtrBTNode Root;
  22. };
  23. PtrBTNode BTAllocateNode();
  24. PtrBTNode BTSearch(PtrBTNode Root, ElementType Val, int* Index);
  25. void BTChildSplit(PtrBTNode SplitNodeP, int ChildIndex);
  26. void BTInsertNonFull(PtrBTNode Root, ElementType Val);
  27. void BTInsert(PtrBT T, ElementType Val);
  28. void BTDelete(PtrBT T, PtrBTNode Root, ElementType Val);
  29. void Merge(PtrBT T, PtrBTNode ParentNode, int LeftIndex, int RightIndex);
  30. void ShiftKey(PtrElementType Key, Bool Direction, int Begin, int End);
  31. void ShiftChild(PtrBTNode *Child, Bool Direction, int Begin, int End);
  32. int GetIndex(PtrElementType Key, int Size, ElementType Val);
  33. void BTPrintTree(PtrBTNode Root);
  34. void BTCreateTree(PtrBT T);
  35. //A test program
  36. int main(){
  37. PtrBT T = (PtrBT)malloc(sizeof(struct Tree));
  38. T->Root = BTAllocateNode();
  39. BTCreateTree(T);
  40. printf("B_Tree after delete 16:\n");
  41. BTDelete(T, T->Root, 16);
  42. BTPrintTree(T->Root);
  43. printf("B_Tree after delete 18:\n");
  44. BTDelete(T, T->Root, 18);
  45. BTPrintTree(T->Root);
  46. printf("B_Tree after delete 20:\n");
  47. BTDelete(T, T->Root, 20);
  48. BTPrintTree(T->Root);
  49. printf("B_Tree after delete 19:\n");
  50. BTDelete(T, T->Root, 19);
  51. BTPrintTree(T->Root);
  52. printf("B_Tree after delete 0:\n");
  53. BTDelete(T, T->Root, 0);
  54. BTPrintTree(T->Root);
  55. printf("B_Tree after delete 5:\n");
  56. BTDelete(T, T->Root, 5);
  57. BTPrintTree(T->Root);
  58. printf("B_Tree after delete 2:\n");
  59. BTDelete(T, T->Root, 2);
  60. BTPrintTree(T->Root);
  61. return 0;
  62. }
  63. PtrBTNode BTAllocateNode(){
  64. int i;
  65. PtrBTNode NewNode = (PtrBTNode)malloc(sizeof(BTNode));
  66. NewNode->Num = 0;
  67. NewNode->IsLeaf = True;
  68. NewNode->Key = (PtrElementType)malloc(sizeof(ElementType) * (MinDegree * 2 - 1));
  69. NewNode->Child = (PtrBTNode*)malloc(sizeof(PtrBTNode) * (MinDegree * 2));
  70. for(i = 0; i < MinDegree * 2; i++){
  71. NewNode->Child[i] = NULL;
  72. }
  73. return NewNode;
  74. }
  75. PtrBTNode BTSearch(PtrBTNode Root, ElementType Val, int* Index){
  76. int i;
  77. for(i = 0; i < Root->Num&&Val > Root->Key[i]; i++){
  78. ;
  79. }
  80. if(i < Root->Num&&Root->Key[i] == Val){
  81. *Index = i;
  82. return Root;
  83. }
  84. else if(True == Root->IsLeaf){
  85. return NULL;
  86. }
  87. else{
  88. return BTSearch(Root->Child[i], Val, Index);
  89. }
  90. }
  91. void BTChildSplit(PtrBTNode SplitNodeP, int ChildIndex){
  92. int i;
  93. PtrBTNode NewNode = BTAllocateNode();
  94. PtrBTNode FullNode = SplitNodeP->Child[ChildIndex];
  95. for(i = 0; i < MinDegree - 1; i++){
  96. NewNode->Key[i] = FullNode->Key[MinDegree + i];
  97. }
  98. if(False == FullNode->IsLeaf){
  99. NewNode->IsLeaf = False;
  100. for(i = 0; i < MinDegree; i++){
  101. NewNode->Child[i] = FullNode->Child[MinDegree + i];
  102. }
  103. }
  104. NewNode->Num = FullNode->Num = MinDegree - 1;
  105. ShiftKey(SplitNodeP->Key, 1, ChildIndex, SplitNodeP->Num - 1);
  106. SplitNodeP->Key[ChildIndex] = FullNode->Key[MinDegree - 1];
  107. ShiftChild(SplitNodeP->Child, 1, ChildIndex + 1, SplitNodeP->Num);
  108. SplitNodeP->Child[ChildIndex + 1] = NewNode;
  109. (SplitNodeP->Num)++;
  110. }
  111. void BTInsertNonFull(PtrBTNode CurrentNode, ElementType Val){
  112. int Index = GetIndex(CurrentNode->Key, CurrentNode->Num, Val);
  113. if(True == CurrentNode->IsLeaf){
  114. ShiftKey(CurrentNode->Key, 1, Index, CurrentNode->Num - 1);
  115. CurrentNode->Key[Index] = Val;
  116. (CurrentNode->Num)++;
  117. }
  118. else{
  119. if(MinDegree * 2 - 1 == CurrentNode->Child[Index]->Num){
  120. BTChildSplit(CurrentNode, Index);
  121. if(CurrentNode->Key[Index] < Val){
  122. Index++;
  123. }
  124. }
  125. BTInsertNonFull(CurrentNode->Child[Index], Val);
  126. }
  127. }
  128. void BTInsert(PtrBT T, ElementType Val){
  129. PtrBTNode NewNode;
  130. if(MinDegree * 2 - 1 == T->Root->Num){
  131. NewNode = BTAllocateNode();
  132. NewNode->IsLeaf = False;
  133. NewNode->Child[0] = T->Root;
  134. T->Root = NewNode;
  135. BTChildSplit(NewNode, 0);
  136. }
  137. BTInsertNonFull(T->Root, Val);
  138. }
  139. void BTDelete(PtrBT T, PtrBTNode CurrentNode, ElementType Val){
  140. int Index;
  141. PtrBTNode Precursor, Successor, SubNode;
  142. Index = GetIndex(CurrentNode->Key, CurrentNode->Num, Val);
  143. SubNode = CurrentNode->Child[Index];
  144. if(Index < CurrentNode->Num && CurrentNode->Key[Index] == Val){
  145. if(True == CurrentNode->IsLeaf){
  146. ShiftKey(CurrentNode->Key, 0, Index + 1, CurrentNode->Num - 1);
  147. (CurrentNode->Num)--;
  148. return;
  149. }
  150. else{
  151. Precursor = CurrentNode->Child[Index];
  152. Successor = CurrentNode->Child[Index + 1];
  153. if(Precursor->Num > MinDegree - 1){
  154. CurrentNode->Key[Index] = Precursor->Key[Precursor->Num - 1];
  155. BTDelete(T, Precursor, Precursor->Key[Precursor->Num - 1]);
  156. }
  157. else if(Successor->Num > MinDegree - 1){
  158. CurrentNode->Key[Index] = Successor->Key[0];
  159. BTDelete(T, Successor, Successor->Key[0]);
  160. }
  161. else{
  162. Merge(T, CurrentNode, Index, Index + 1);
  163. BTDelete(T, CurrentNode->Child[Index], Val);
  164. }
  165. }
  166. }
  167. else{
  168. if(True == CurrentNode->IsLeaf){
  169. return;
  170. }
  171. else{
  172. if(SubNode->Num > MinDegree - 1){
  173. BTDelete(T, SubNode, Val);
  174. }
  175. else{
  176. if(Index > 0){
  177. Precursor = CurrentNode->Child[Index - 1];
  178. }
  179. if(Index < CurrentNode->Num){
  180. Successor = CurrentNode->Child[Index + 1];
  181. }
  182. if(Index > 0 && Precursor->Num > MinDegree - 1){
  183. ShiftKey(SubNode->Key, 1, 0, SubNode->Num - 1);
  184. ShiftChild(SubNode->Child, 1, 0, SubNode->Num);
  185. SubNode->Key[0] = CurrentNode->Key[Index - 1];
  186. SubNode->Child[0] = Precursor->Child[Precursor->Num];
  187. (SubNode->Num)++;
  188. CurrentNode->Key[Index - 1] = Precursor->Key[Precursor->Num - 1];
  189. (Precursor->Num)--;
  190. BTDelete(T, SubNode, Val);
  191. }
  192. else if(Index < CurrentNode->Num && Successor->Num > MinDegree - 1){
  193. SubNode->Key[SubNode->Num] = CurrentNode->Key[Index];
  194. SubNode->Child[SubNode->Num + 1] = Successor->Child[0];
  195. (SubNode->Num)++;
  196. CurrentNode->Key[Index] = Successor->Key[0];
  197. ShiftKey(Successor->Key, 0, 1, Successor->Num - 1);
  198. ShiftChild(Successor->Child, 0, 1, Successor->Num);
  199. (Successor->Num)--;
  200. BTDelete(T, CurrentNode->Child[Index], Val);
  201. }
  202. else{
  203. if(Index > 0){
  204. Merge(T, CurrentNode, Index - 1, Index);
  205. BTDelete(T, Precursor, Val);
  206. }
  207. else{
  208. Merge(T, CurrentNode, Index, Index + 1);
  209. BTDelete(T, SubNode, Val);
  210. }
  211. }
  212. }
  213. }
  214. }
  215. }
  216. void Merge(PtrBT T, PtrBTNode ParentNode, int LeftIndex, int RightIndex){
  217. PtrBTNode LeftNode = ParentNode->Child[LeftIndex], RightNode = ParentNode->Child[RightIndex];
  218. int i;
  219. for(i = 0; i < MinDegree - 1; i++){
  220. LeftNode->Key[MinDegree + i] = RightNode->Key[i];
  221. }
  222. if(False == LeftNode->IsLeaf){
  223. for(i = 0; i < MinDegree; i++){
  224. LeftNode->Child[MinDegree + i] = RightNode->Child[i];
  225. }
  226. }
  227. LeftNode->Key[MinDegree - 1] = ParentNode->Key[LeftIndex];
  228. LeftNode->Num = MinDegree * 2 - 1;
  229. ShiftKey(ParentNode->Key, 0, LeftIndex + 1, ParentNode->Num - 1);
  230. ShiftChild(ParentNode->Child, 0, RightIndex + 1, ParentNode->Num);
  231. (ParentNode->Num)--;
  232. if(ParentNode == T->Root && 0 == ParentNode->Num){
  233. T->Root = LeftNode;
  234. }
  235. }
  236. void ShiftKey(PtrElementType Key, Bool Direction, int Begin, int End){
  237. int i;
  238. if(True == Direction){
  239. for(i = End; i >= Begin; i--){
  240. Key[i + 1] = Key[i];
  241. }
  242. }
  243. else{
  244. for(i = Begin; i <= End; i++){
  245. Key[i - 1] = Key[i];
  246. }
  247. }
  248. }
  249. void ShiftChild(PtrBTNode *Child, Bool Direction, int Begin, int End){
  250. int i;
  251. if(True == Direction){
  252. for(i = End; i >= Begin; i--){
  253. Child[i + 1] = Child[i];
  254. }
  255. }
  256. else{
  257. for(i = Begin; i <= End; i++){
  258. Child[i - 1] = Child[i];
  259. }
  260. }
  261. }
  262. int GetIndex(PtrElementType Key, int Size, ElementType Val){
  263. int i;
  264. for(i = 0; i < Size; i++){
  265. if(Key[i] >= Val){
  266. break;
  267. }
  268. }
  269. return i;
  270. }
  271. void BTPrintTree(PtrBTNode Root){
  272. int i;
  273. if(NULL == Root){
  274. return;
  275. }
  276. putchar('[');
  277. for(i = 0; i < Root->Num; i++){
  278. printf("%d", Root->Key[i]);
  279. if(i != Root->Num - 1){
  280. putchar(' ');
  281. }
  282. }
  283. putchar(']');
  284. printf("%d", Root->IsLeaf);
  285. putchar('\n');
  286. for(i = 0; i <= Root->Num; i++){
  287. BTPrintTree(Root->Child[i]);
  288. }
  289. }
  290. void BTCreateTree(PtrBT T){
  291. int i;
  292. int a[] = {12,1,9,2,0,11,7,19,4,15,18,5,14,13,10,16,6,3,8,17,20,21,23};
  293. for(i = 0; i < 23; i++){
  294. BTInsert(T, a[i]);
  295. BTPrintTree(T->Root);
  296. printf("The End\n");
  297. }
  298. }

B树的查找、插入、删除(附源代码)的更多相关文章

  1. 数据结构与算法->树->2-3-4树的查找,添加,删除(Java)

    代码: 兵马未动,粮草先行 作者: 传说中的汽水枪 如有错误,请留言指正,欢迎一起探讨. 转载请注明出处. 目录 一. 2-3-4树的定义 二. 2-3-4树数据结构定义 三. 2-3-4树的可以得到 ...

  2. 数据结构系列之2-3-4树的插入、查找、删除和遍历完整版源代码实现与分析(dart语言实现)

    本文属于原创,转载请注明来源. 在上一篇博文中,详细介绍了2-3树的操作(具体地址:https://www.cnblogs.com/outerspace/p/10861488.html),那么对于更多 ...

  3. B树之C语言实现(包含查找、删除、插入)

    B树的定义 一棵m阶B树(Balanced Tree of order m),或为空树,或为满足下列特性对的m叉树. 树中每个结点最多含有m棵子树. 若根结点不是叶子结点,则至少有2个子树. 除根结点 ...

  4. AVL树(查找、插入、删除)——C语言

    AVL树 平衡二叉查找树(Self-balancing binary search tree)又被称为AVL树(AVL树是根据它的发明者G. M. Adelson-Velskii和E. M. Land ...

  5. DS-二叉排序树的插入、查找和删除

    2019-12-02(菜鸡开始学习了...) Data Structure 之 二叉排序树 二叉排序树是给定一个节点后,接下来插入的数如果比它大就会放到它的右孩子那边,比它小就会放到它的左孩子那边. ...

  6. B-树的插入、查找、删除

    转自:http://blog.163.com/zhoumhan_0351/blog/static/39954227200910231032917/ 前面讨论的查找都是内查询算法,被查询的数据都在内存. ...

  7. 【数据结构】——搜索二叉树的插入,查找和删除(递归&非递归)

    一.搜索二叉树的插入,查找,删除 简单说说搜索二叉树概念: 二叉搜索树又称二叉排序树,它或者是一棵空树,或者是具有以下性质的二叉树 若它的左子树不为空,则左子树上所有节点的值都小于根节点的值 若它的右 ...

  8. Java实现二叉排序树的插入、查找、删除

    import java.util.Random; /** * 二叉排序树(又称二叉查找树) * (1)能够是一颗空树 * (2)若左子树不空,则左子树上全部的结点的值均小于她的根节点的值 * (3)若 ...

  9. python Trie树和双数组TRIE树的实现. 拥有3个功能:插入,删除,给前缀智能找到所有能匹配的单词

    #coding=utf- #字典嵌套牛逼,别人写的,这样每一层非常多的东西,搜索就快了,树高26.所以整体搜索一个不关多大的单词表 #还是O(). ''' Python 字典 setdefault() ...

  10. 七大查找算法(附C语言代码实现)

    来自:Poll的笔记 - 博客园 链接:http://www.cnblogs.com/maybe2030/p/4715035.html 阅读目录 1.顺序查找 2.二分查找 3.插值查找 4.斐波那契 ...

随机推荐

  1. js优化与注意点

    1.使用尽可能少的全局变量.尽量用var来声明变量,避免隐式使用全局变量. 隐式全局变量和明确定义的全局变量间有些小的差异,就是通过delete操作符让变量未定义的能力. 通过var创建的全局变量(任 ...

  2. $.when().done().then()的用法

    jQuery的开发速度很快,几乎每半年一个大版本,每两个月一个小版本. 每个版本都会引入一些新功能.今天我想介绍的,就是从jQuery 1.5.0版本开始引入的一个新功能----deferred对象. ...

  3. (简单) POJ 2253 Frogger,Dijkstra。

    Description Freddy Frog is sitting on a stone in the middle of a lake. Suddenly he notices Fiona Fro ...

  4. 25、手把手教你Extjs5(二十五)Extjs5常用组件--form的基本用法

    Extjs Form是一个比较常用的控件,主要用来显示和编辑数据的,今天这篇文章将介绍Extjs Form控件的详细用法,包括创建Form.添加子项.加载和更新数据.验证等. Form和Form Ba ...

  5. AFNetworking封装思路简析

    http://blog.csdn.net/qq_34101611/article/details/51698473 一.AFNetworking的发展 1. AFN 1.0版本 AFN 的基础部分是 ...

  6. iOS开发——UIImageView

    1.图像点击之后,全屏浏览 - (void)viewDidLoad { [super viewDidLoad]; _myImage=[[UIImageView alloc]initWithFrame: ...

  7. 《算法导论》习题2.3-5 二分搜索 Binary Search

    地球人都知道“二分查找”,方法也非常简单,但是你能不能在10分钟内写出一个没有bug的程序呢? 知易行难,自己动手写一下试一试吧. public class BinarySearch { public ...

  8. S3C2440时钟系统详解

    在讲述系统时钟之前,因为这些设备都是挂靠在系统时钟上的,所以必须先说系统时钟,S3C2440的时钟系统如下 外部时钟源分两种,晶振或者外部频率,由om3-2选择,时钟电路根据两种选择也有两种 我们来分 ...

  9. 02 easyui -parser

    parser: 页面节点class=“easyui-” +组件名(在plugins里) ,则可以 渲染成相应的组件.

  10. 【转】git命令

    Git使用 1. git pull    更新服务器代码到本地a). git pull origin master是将origin这个版本库的代码更新到本地的master主分支 2. git push ...