编程练习——伸展树(SplayTree)

伸展树用于查询结果的概率非平均情况,即起初假设某些要查询的信息偏多,有些信息几乎无人问津。对于平均访问概率来说,使用SplayTree实现并不是一个明智的选
择。

可能会用到此数据结构的程序:词典或者多维搜索树,还有连接/切除树(link/cut tree)(这个树是用于网络优化问题包括最大流和最小代价)

  1. /*
  2. * create by chico chen
  3. * date: 2008/10/02
  4. */
  5. template < class T>
  6. class SplayTree: public BinaryTree
  7. {
    1. public :
  8. explicit SplayTree(T& data):BinaryTree(data)
  9. {
    1. }
    1. explicit SplayTree(TreeNode* root):BinaryTree(root)
  10. {
    1. }
  11. ~SplayTree()
  12. {
    1. }
  13. public :
    1. void insertNode(T& data)
  14. {
    1. if (NULL == this ->head)
  15. {
  16. this ->head = new TreeNode(data);
  17. return ;
  18. }
    1. splay( this ->head,data);
  19. if ( this ->head->data > data)
  20. {
  21. //     A     B                 A
  22. //            / /     ->         / /
  23. //         C   D             C   B
  24. //                                /
  25. //                                 D
  26. TreeNode* node = new TreeNode(data);
  27. node->left = this ->head->left ;
  28. node->right = this ->head;
  29. this ->head->left = NULL;
  30. this ->head = node;
  31. }
  32. else if ( this ->head->data < data)
  33. {
  34. //     A     B                 A
  35. //            / /     ->         / /
  36. //         C   D             B   D
  37. //                              /
  38. //                            C
  39. TreeNode* node = new TreeNode(data);
  40. node->right = this ->head->right;
  41. node->left = this ->head;
  42. this ->head->right = NULL;
  43. this ->head = node;
    1. }
  44. else
  45. {
  46. // insert the same key
  47. // throw exception
  48. throw “the same key” ;
  49. }
  50. }
    1. // use the data as key to splay the tree
  51. // then we can get the structures of the tree like the following:
  52. //     (1)  A             (2)   A
  53. //              /                    / /
  54. //               B               C   B
  55. // the difference between the two structures is whether the root has left child
  56. // In case (1), we can simpliy make the root right child instead the root.
  57. // In case (2), we should splay the root left subtree and then it will be
  58. // changed like case (1), and we make the splayed left subtree root instead
  59. // the root.
  60. void deleteNode(T& data)
  61. {
    1. if (NULL == this ->head)
  62. {
  63. return ;
  64. }
  65. splay( this ->head, data);
  66. if ( this ->head->data != data)
  67. {
  68. // not found the key to delete
  69. return ;
  70. }
  71. TreeNode* newRoot;
  72. if (NULL == this ->head->left)
  73. {
  74. newRoot = this ->head->right;
  75. }
  76. else
  77. {
  78. newRoot = this ->head->left;
  79. splay(newRoot,data);
  80. newRoot->right = this ->head->right;
  81. }
  82. delete this ->head;
  83. this ->head = newRoot;
    1. }
      1. TreeNode* searchNode(T& data)
  84. {
  85. if (NULL == this ->head)
  86. {
  87. return NULL;
  88. }
  89. splay( this ->head, data);
  90. if ( this ->head->data == data)
  91. {
  92. return this ->head;
  93. }
  94. return NULL;
  95. }
    1. private :
  96. // use top-down splay method,
    1. // you should make the suitable final step
  97. // according to the situation, such as “insert”, “delete”, “search”.
  98. // And what’s the top-down method?
  99. // now we should add LeftMaxRoot as L and RightMinRoot as R
  100. // 1. L  A  R         2. L  A  R        3. L   A   R
  101. //         /                        /                      /
  102. //        B                     B                     B
  103. //                               /                          /
  104. //                              C                          C
  105. //
  106. // 1.->L  B  R      2.->L  C   R        3.L   C    R
  107. //        /                     /                          /      /
  108. //     A                    B                         B    A
  109. //                             /
  110. //                              A
  111. // but in case 3. we can simplified case 3 rotate as
  112. // 3. L   B      R
  113. //            /      /
  114. //            C  A
  115. // and we must integrate the LeftMaxRoot and RightMinRoot together,
  116. // then we have final step to do this. And L is left tree, and R is right tree.
  117. // L     A     R               A
  118. //         / /         ->         /   /
  119. //       B   C                L     R
  120. //                                 /   /
  121. //                                B C
  122. void splay(TreeNode* &subRoot,T& data)
  123. {
  124. if (NULL == subRoot)
  125. {
  126. return ;
  127. }
  128. TreeNode* leftRoot;
  129. TreeNode* rightRoot;
  130. // here some code use static member, if you promise you have sync-control
  131. // or you don’t use this code in thread or process, you could choose static
  132. // member to accelerate your program.
  133. // It means the following code is thread-safe.
  134. TreeNode head(data);
  135. leftRoot = rightRoot = &head;
  136. while ( true )
  137. {
    1. if (data < subRoot->data)
  138. {
  139. if (NULL == subRoot->left)
  140. {
  141. break ;
  142. }
  143. else if (data < subRoot->left->data)
  144. {
  145. // Left rotate
  146. TreeNode* tempLeft = subRoot->left;
  147. subRoot->left = tempLeft->right;
  148. tempLeft->right = subRoot;
  149. subRoot = tempLeft;
    1. }
  150. else
  151. {
  152. // do nothing
  153. }
  154. // split the tree with right root
  155. if (NULL == subRoot->left)
  156. break ;
  157. rightRoot->left = subRoot;
  158. rightRoot = subRoot;
  159. subRoot = subRoot->left ;
    1. }
  160. else if (data > subRoot->data)
  161. {
  162. if (NULL == subRoot->right)
  163. {
  164. // need not to rotate
  165. break ;
  166. }
  167. else if (data > subRoot->right->data)
  168. {
  169. // right rotate
  170. TreeNode* tempRight = subRoot->right;
  171. subRoot->right = tempRight->left;
  172. tempRight->left = subRoot;
  173. subRoot = tempRight;
  174. }
  175. else
  176. {
  177. // do nothing
  178. }
  179. // split the tree by left root
  180. if (NULL == subRoot->right)
  181. break ;
  182. leftRoot->right = subRoot;
  183. leftRoot = subRoot;
    1. subRoot = subRoot->right;
  184. }
  185. else
  186. {
  187. // the same key
  188. break ;
  189. }
    1. }
    1. // the final step
  190. // we have find the last node, ant then we should
  191. // integrate the left root, the right root and the root into One tree.
  192. // head.right is left root
  193. // head.left is right root
  194. leftRoot->right = subRoot->left;
  195. rightRoot->left = subRoot->right;
  196. subRoot->left = head.right;
  197. subRoot->right = head.left;
      1. }
          1. };

发现CSDN的代码编辑器还是有些问题,空格处理不好,导致我注释中的树形错乱了…

不过没有大关系,应该抓起旁边的纸边看边画就能明白。

在网上找splayTree的源码学习,发现凭自己的实力还真的看不懂…

幸好在家里翻到了古老的数据结构教材。这才明白。

至于不使用添加父节点或者采用递归栈等方式的原因在于:空间消耗太大。

另外递归栈也不好调试

这个top-down splay的方法消耗空间为O(1),带来的效果却和添加父节点类似,没有使用递归的方法。

至于具体的方法,在代码的注释里。

这里为了编代码方便就没有将treeNode 类中的成员变量私有化。

另外的BinaryTree的代码在 _ 以前的blog中 _

这个splayTree还是较为完整的,至少比Apache中的splayTree完整些。现在我依旧不明白为什么Apache中的内存管理使用的是splayTre
e,而且splayTree还没有delete函数。难道内存管理的东西没有必要删除吗?

思考中…