• Day13:数据结构之(B-树)2-3树


    目录

    一、基本概念

            1.与AVL树进行比较:

            2.基本原理和插入操作の示意图

                    ①23树有三种节点

    ​                ②4节点一旦出现,需要拆分   从下往上

                    插入元素的流程:         

    二、2-3树插入代码实现

    一、基本概念

            1.与AVL树进行比较:

                    2-3树增删操作的次数比AVL树相对较少一点

                    保证查找效率的前提之下优化增删效率

            2.基本原理和插入操作の示意图

                    ①23树有三种节点

                             2节点 3节点 4节点;4节点只是临时状态


                    ②4节点一旦出现,需要拆分   从下往上


                             数据进来 先往下沉   再往上升

                    插入元素的流程:         

            如果是空树,创建一个节点即可。

            如果不是空树,插入的情况分为4种:

                    ①.向2-节点中插入元素;  

    如果未命中查找结束于2节点,直接将2节点替换为3节点,并将待插入元素添加到其中。

             file

                    ②.向一颗只含有一个3节点的树中插入元素;

            如果查找结束于3节点,先临时将其成为4节点,把待插入元素添加到其中,然后将4节点转化为3个2节点,中间的节点成为左右节点的父节点。如果之前临时4节点有父节点,就会变成向一个父节点为2节点的3节点中插入元素,中间节点与父节点为2节点的合并。

             file 

                    ③.向一个父节点为2节点的3节点中插入元素;

                     正常上浮即可。

                    ④.向一个父节点为3节点的3节点中插入元素。

            插入元素后一直向上分解临时的4节点,直到遇到2-节点的父节点变成3节点不再分解。如果达到树根节点还是4节点,则进行分解根节点

              file

    二、2-3树插入代码实现 

            每次函数进来,

            (1)优先处理三种情况:空节点、2节点、3节点(方法:根据该节点在对应位置是否有孩子->有则进行递归调用,无则看大小位置放入即可)

            (2)->函数体的第二部分紧接着检查是否产生了4节点(每次递归出了上一层的函数就会进行相应的检查并及时做出处理(所以在前面讨论的时候,是没有对4节点的处理的)

            对4节点的处理(最基本要新建两个结点node1、node2存储left和right的值以及指针,然后保存中间的值temp)

            ->紧接着判断parent是否为nullptr

                    ---是,则memset当前节点的值和指针,将刚才临时保存的temp放入,然后左右指针分别指向刚才开node1和node2即可。

                    ---否,则需要进行上浮的操作(父亲只可能是2节点or3节点)

                       ①若temp需要放在parent的第一个位置(parent最起码得有一个指针!(指向孩子))

                   此中分情况->parent有无第三个指针(若有代表有第二个孩子,为3节点)即可。然后进行移动与对node1和node2的连接

                       ②若temp需要放在父节点的第二个位置

                            (第一行判断是2节点,第二行判断是3节点)

                     仍然像上面一样,看有无第三个指针(有->代表有第二个孩子,为3节点).

                       ③若temp需要放在parent的第三个位置:

    最终parent记得count++; 并且释放原来的四节点,delete node。

    1. #pragma once
    2. #include
    3. using namespace std;
    4. //0 空节点 1 2节点 2 3节点 3 4节点
    5. enum state{null_node,two_node,three_node,four_node};
    6. template<class T>
    7. class twoThreeTree
    8. {
    9. struct Node
    10. {
    11. int count; //标记当前是 2节点还是3节点还是4节点
    12. T data[3]; //存储数据的数组
    13. Node* pArr[4]; //存储指针的数组
    14. Node()
    15. {
    16. count = null_node;
    17. memset(data, 0, sizeof(T) * 3);
    18. memset(pArr, 0, sizeof(Node*) * 4);
    19. }
    20. };
    21. Node* pRoot;//指向根节点
    22. public:
    23. twoThreeTree(){ pRoot = NULL; }
    24. ~twoThreeTree(){}
    25. void insertNode(const T& data);
    26. private:
    27. void _insertNode(Node* node, Node* pParent, const T& data);
    28. };
    29. template<class T>
    30. void twoThreeTree::insertNode(const T& data)
    31. {
    32. if (pRoot)
    33. {
    34. _insertNode(pRoot, NULL, data);
    35. }
    36. else
    37. {//当前树为空树
    38. pRoot = new Node;
    39. pRoot->data[0] = data;
    40. pRoot->count = two_node;
    41. }
    42. }
    43. template<class T>
    44. void twoThreeTree::_insertNode(Node* node, Node* pParent, const T& data)
    45. {
    46. if (null_node == node->count)
    47. {//当前节点为空
    48. node->data[0] = data;
    49. node->count++;
    50. return;//注意此处的return
    51. }
    52. if (two_node == node->count)
    53. {//当前节点为2节点
    54. if (data > node->data[0])
    55. {//新数据比当前数据大 往右边插入
    56. if (node->pArr[1])
    57. {//当前节点有右孩子
    58. _insertNode(node->pArr[1], node, data);
    59. }
    60. else
    61. {//当前节点没有左孩子
    62. node->data[1] = data;
    63. node->count++;
    64. }
    65. }
    66. else
    67. {//往左边插入
    68. if (node->pArr[0])
    69. {//当前节点有左孩子
    70. _insertNode(node->pArr[0], node, data);
    71. }
    72. else
    73. {//当前节点没有孩子
    74. //原来数据往右挪
    75. node->data[1] = node->data[0];
    76. //新数据进来
    77. node->data[0] = data;
    78. //节点变化
    79. node->count++;
    80. }
    81. }
    82. }
    83. else
    84. {//当前节点是3节点
    85. if (data < node->data[0])
    86. {//往最左边插入 图上的情况3
    87. if (node->pArr[0])
    88. {//有孩子
    89. _insertNode(node->pArr[0], node, data);
    90. }
    91. else{//没有孩子
    92. //中间的放右边
    93. node->data[2] = node->data[1];
    94. //前面的放中间
    95. node->data[1] = node->data[0];
    96. //新数据放前面
    97. node->data[0] = data;
    98. //节点变化
    99. node->count++;
    100. }
    101. }
    102. else if (data < node->data[1])
    103. {//往中间插入 图上的情况2
    104. if (node->pArr[1])
    105. {//有孩子
    106. _insertNode(node->pArr[1], node, data);
    107. }
    108. else
    109. {//没有孩子
    110. //中间的放右边
    111. node->data[2] = node->data[1];
    112. //新数据放中间
    113. node->data[1] = data;
    114. //节点变化
    115. node->count++;
    116. }
    117. }
    118. else{//往右边插入 图上的情况1
    119. if (node->pArr[2])
    120. {//有孩子
    121. _insertNode(node->pArr[2], node, data);
    122. }
    123. else{//没有孩子
    124. //新数据放右边
    125. node->data[2] = data;
    126. //节点变化
    127. node->count++;
    128. }
    129. }
    130. }
    131. //每次递归回溯的时候,都会检查是否产生了4节点
    132. if (four_node == node->count){//如果产生了4节点
    133. //1 创建两个节点
    134. Node* node1 = new Node;//左
    135. Node* node2 = new Node;//右
    136. //node1是左边的
    137. node1->data[0] = node->data[0];
    138. node1->pArr[0] = node->pArr[0];
    139. node1->pArr[1] = node->pArr[1];
    140. node1->count = two_node;
    141. //node2是右边的
    142. node2->data[0] = node->data[2];
    143. node2->pArr[0] = node->pArr[2];
    144. node2->pArr[1] = node->pArr[3];
    145. node2->count = two_node;
    146. //2 临时存储中间值
    147. T temp = node->data[1];
    148. if (pParent)
    149. {//当前节点有父节点
    150. //5 找位置 做插入(分为三个位置)
    151. if (temp < pParent->data[0])
    152. {//左边
    153. if (pParent->pArr[2])
    154. {//最右边有孩子
    155. pParent->data[2] = pParent->data[1];
    156. pParent->data[1] = pParent->data[0];
    157. pParent->data[0] = temp;
    158. pParent->pArr[3] = pParent->pArr[2];
    159. pParent->pArr[2] = pParent->pArr[1];
    160. pParent->pArr[1] = node2;
    161. pParent->pArr[0] = node1;
    162. }
    163. else if(pParent->pArr[1])
    164. {//最右边没有孩子,中间有孩子
    165. pParent->data[1] = pParent->data[0];
    166. pParent->data[0] = temp;
    167. pParent->pArr[2] = pParent->pArr[1];
    168. pParent->pArr[1] = node2;
    169. pParent->pArr[0] = node1;
    170. }
    171. }
    172. else if (two_node == pParent->count || //这边利用短路特性
    173. (pParent->count > 1)&&temp < pParent->data[1])
    174. {//temp需要放中间(因为此层的else if已经避开temp小于第一个元素的情况,那么必定是大于第一个元素,而小于第二个元素)
    175. if (pParent->pArr[2])
    176. {//最右边有孩子
    177. pParent->data[2] = pParent->data[1];
    178. pParent->data[1] = temp;
    179. pParent->pArr[3] = pParent->pArr[2];
    180. pParent->pArr[2] = node2;
    181. pParent->pArr[1] = node1;
    182. }
    183. else if (pParent->pArr[1])
    184. {//3节点的最右边无孩子,中间有孩子
    185. pParent->data[1] = temp;
    186. pParent->pArr[2] = node2;
    187. pParent->pArr[1] = node1;
    188. }
    189. }
    190. else if (three_node == pParent->count ||
    191. (pParent->count > 2) && (temp < pParent->data[2]))
    192. {//右边
    193. if (pParent->pArr[2])
    194. {
    195. pParent->data[2] = temp;
    196. pParent->pArr[3] = node2;
    197. pParent->pArr[2] = node1;
    198. }
    199. }
    200. pParent->count++;
    201. //6 释放内存
    202. delete node;
    203. }
    204. else
    205. {//当前节点没有父节点
    206. //3 当前节点变成2节点
    207. memset(node->data, 0, sizeof(T)* 3);//清空数据
    208. memset(node->pArr, 0, sizeof(Node*)* 4);//清空指针
    209. node->data[0] = temp;
    210. node->count = two_node;
    211. //4 node1成为当前节点的左孩子 node2成为当前节点的右孩子
    212. node->pArr[0] = node1; node->pArr[1] = node2;
    213. }
    214. }
    215. }

    test:

  • 相关阅读:
    NB的Github项目,看到最后一个我惊呆了!
    【发表案例】网络智能类SCI&EI,仅25天录用,录用后15天见刊,见刊后16天检索
    React报错之React hook ‘useState‘ cannot be called in a class component
    vue2.x版本中computed和watch的使用入门详解-watch篇
    linux安装jdk1.8并启动jar包(又一次配置环境,简单记录下,要是小白,刚接触,按照步骤来即可)
    50、Flink 数据源的事件时间和水印详解
    性能测试面试问题,一周拿3个offer不嫌多
    linux共享文件问题
    Double4 VR智能互动教学应用系统演示
    Oracle EBS form开发 提示 FRM-15500:Valid and unique object name must be entered
  • 原文地址:https://blog.csdn.net/zjjaibc/article/details/126194415