编程练习——伸展树(SplayTree)
伸展树用于查询结果的概率非平均情况,即起初假设某些要查询的信息偏多,有些信息几乎无人问津。对于平均访问概率来说,使用SplayTree实现并不是一个明智的选
择。
可能会用到此数据结构的程序:词典或者多维搜索树,还有连接/切除树(link/cut tree)(这个树是用于网络优化问题包括最大流和最小代价)
- /*
- * create by chico chen
- * date: 2008/10/02
- */
- template < class T>
- class SplayTree: public BinaryTree
- {
-
- public :
- explicit SplayTree(T& data):BinaryTree(data)
- {
-
- }
-
- explicit SplayTree(TreeNode
* root):BinaryTree(root)
- explicit SplayTree(TreeNode
- {
-
- }
- ~SplayTree()
- {
-
- }
- public :
-
- void insertNode(T& data)
- {
-
- if (NULL == this ->head)
- {
- this ->head = new TreeNode
(data); - return ;
- }
-
- splay( this ->head,data);
- if ( this ->head->data > data)
- {
- // A B A
- // / / -> / /
- // C D C B
- // /
- // D
- TreeNode
* node = new TreeNode (data); - node->left = this ->head->left ;
- node->right = this ->head;
- this ->head->left = NULL;
- this ->head = node;
- }
- else if ( this ->head->data < data)
- {
- // A B A
- // / / -> / /
- // C D B D
- // /
- // C
- TreeNode
* node = new TreeNode (data); - node->right = this ->head->right;
- node->left = this ->head;
- this ->head->right = NULL;
- this ->head = node;
-
- }
- else
- {
- // insert the same key
- // throw exception
- throw “the same key” ;
- }
- }
-
- // use the data as key to splay the tree
- // then we can get the structures of the tree like the following:
- // (1) A (2) A
- // / / /
- // B C B
- // the difference between the two structures is whether the root has left child
- // In case (1), we can simpliy make the root right child instead the root.
- // In case (2), we should splay the root left subtree and then it will be
- // changed like case (1), and we make the splayed left subtree root instead
- // the root.
- void deleteNode(T& data)
- {
-
- if (NULL == this ->head)
- {
- return ;
- }
- splay( this ->head, data);
- if ( this ->head->data != data)
- {
- // not found the key to delete
- return ;
- }
- TreeNode
* newRoot; - if (NULL == this ->head->left)
- {
- newRoot = this ->head->right;
- }
- else
- {
- newRoot = this ->head->left;
- splay(newRoot,data);
- newRoot->right = this ->head->right;
- }
- delete this ->head;
- this ->head = newRoot;
-
- }
-
-
- TreeNode
* searchNode(T& data)
- TreeNode
-
- {
- if (NULL == this ->head)
- {
- return NULL;
- }
- splay( this ->head, data);
- if ( this ->head->data == data)
- {
- return this ->head;
- }
- return NULL;
- }
-
- private :
- // use top-down splay method,
-
- // you should make the suitable final step
- // according to the situation, such as “insert”, “delete”, “search”.
- // And what’s the top-down method?
- // now we should add LeftMaxRoot as L and RightMinRoot as R
- // 1. L A R 2. L A R 3. L A R
- // / / /
- // B B B
- // / /
- // C C
- //
- // 1.->L B R 2.->L C R 3.L C R
- // / / / /
- // A B B A
- // /
- // A
- // but in case 3. we can simplified case 3 rotate as
- // 3. L B R
- // / /
- // C A
- // and we must integrate the LeftMaxRoot and RightMinRoot together,
- // then we have final step to do this. And L is left tree, and R is right tree.
- // L A R A
- // / / -> / /
- // B C L R
- // / /
- // B C
- void splay(TreeNode
* &subRoot,T& data) - {
- if (NULL == subRoot)
- {
- return ;
- }
- TreeNode
* leftRoot; - TreeNode
* rightRoot; - // here some code use static member, if you promise you have sync-control
- // or you don’t use this code in thread or process, you could choose static
- // member to accelerate your program.
- // It means the following code is thread-safe.
- TreeNode
head(data); - leftRoot = rightRoot = &head;
- while ( true )
- {
-
- if (data < subRoot->data)
- {
- if (NULL == subRoot->left)
- {
- break ;
- }
- else if (data < subRoot->left->data)
- {
- // Left rotate
- TreeNode
* tempLeft = subRoot->left; - subRoot->left = tempLeft->right;
- tempLeft->right = subRoot;
- subRoot = tempLeft;
-
- }
- else
- {
- // do nothing
- }
- // split the tree with right root
- if (NULL == subRoot->left)
- break ;
- rightRoot->left = subRoot;
- rightRoot = subRoot;
- subRoot = subRoot->left ;
-
- }
- else if (data > subRoot->data)
- {
- if (NULL == subRoot->right)
- {
- // need not to rotate
- break ;
- }
- else if (data > subRoot->right->data)
- {
- // right rotate
- TreeNode
* tempRight = subRoot->right; - subRoot->right = tempRight->left;
- tempRight->left = subRoot;
- subRoot = tempRight;
- }
- else
- {
- // do nothing
- }
- // split the tree by left root
- if (NULL == subRoot->right)
- break ;
- leftRoot->right = subRoot;
- leftRoot = subRoot;
-
- subRoot = subRoot->right;
- }
- else
- {
- // the same key
- break ;
- }
-
- }
-
- // the final step
- // we have find the last node, ant then we should
- // integrate the left root, the right root and the root into One tree.
- // head.right is left root
- // head.left is right root
- leftRoot->right = subRoot->left;
- rightRoot->left = subRoot->right;
- subRoot->left = head.right;
- subRoot->right = head.left;
-
-
- }
-
-
-
-
-
- };
-
-
-
发现CSDN的代码编辑器还是有些问题,空格处理不好,导致我注释中的树形错乱了…
不过没有大关系,应该抓起旁边的纸边看边画就能明白。
在网上找splayTree的源码学习,发现凭自己的实力还真的看不懂…
幸好在家里翻到了古老的数据结构教材。这才明白。
至于不使用添加父节点或者采用递归栈等方式的原因在于:空间消耗太大。
另外递归栈也不好调试
这个top-down splay的方法消耗空间为O(1),带来的效果却和添加父节点类似,没有使用递归的方法。
至于具体的方法,在代码的注释里。
这里为了编代码方便就没有将treeNode 类中的成员变量私有化。
另外的BinaryTree的代码在 _ 以前的blog中 _
。
这个splayTree还是较为完整的,至少比Apache中的splayTree完整些。现在我依旧不明白为什么Apache中的内存管理使用的是splayTre
e,而且splayTree还没有delete函数。难道内存管理的东西没有必要删除吗?
思考中…