伸展树用于查询结果的概率非平均情况,即起初假设某些要查询的信息偏多,有些信息几乎无人问津。对于平均访问概率来说,使用SplayTree实现并不是一个明智的选择。
可能会用到此数据结构的程序:词典或者多维搜索树,还有连接/切除树(link/cut tree)(这个树是用于网络优化问题包括最大流和最小代价)
- /*
- * create by chico chen
- * date: 2008/10/02
- */
- template<class T>
- class SplayTree:public BinaryTree<T>
- {
- public:
- explicit SplayTree(T& data):BinaryTree(data)
- {
- }
- explicit SplayTree(TreeNode<T>* root):BinaryTree(root)
- {
- }
- ~SplayTree()
- {
- }
- public:
- void insertNode(T& data)
- {
- if(NULL == this->head)
- {
- this->head = new TreeNode<T>(data);
- return;
- }
- splay(this->head,data);
- if(this->head->data > data)
- {
- // A B A
- // / / -> / /
- // C D C B
- // /
- // D
- TreeNode<T>* node = new TreeNode<T>(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<T>* node = new TreeNode<T>(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<T>* 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<T>* searchNode(T& data)
- {
- 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<T>* &subRoot,T& data)
- {
- if(NULL == subRoot)
- {
- return;
- }
- TreeNode<T>* leftRoot;
- TreeNode<T>* 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<T> 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<T>* 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<T>* 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;
- }
- };
发现优快云的代码编辑器还是有些问题,空格处理不好,导致我注释中的树形错乱了...
不过没有大关系,应该抓起旁边的纸边看边画就能明白。
在网上找splayTree的源码学习,发现凭自己的实力还真的看不懂.....
幸好在家里翻到了古老的数据结构教材。这才明白。
至于不使用添加父节点或者采用递归栈等方式的原因在于:空间消耗太大。
另外递归栈也不好调试
这个top-down splay的方法消耗空间为O(1),带来的效果却和添加父节点类似,没有使用递归的方法。
至于具体的方法,在代码的注释里。
这里为了编代码方便就没有将treeNode 类中的成员变量私有化。
另外的BinaryTree的代码在以前的blog中。
这个splayTree还是较为完整的,至少比Apache中的splayTree完整些。现在我依旧不明白为什么Apache中的内存管理使用的是splayTree,而且splayTree还没有delete函数。难道内存管理的东西没有必要删除吗?
思考中....