自顶向下的伸展树

一 伸展树的性质

  伸展树保证从控制开始任意连续M次对树的操作最多花费O(MlogN)时间,一棵伸展树每次操作的摊还(amortized)代价是O(logN)。伸展树的基本思想是,当一个节点被访问后,它就要经过一系列旋转操作被推到根上。另外,伸展树还不要求保留高度或平衡信息。

二 伸展树的伸展操作

  在自底向上的伸展操作的直接实现需要从根沿树往下的一次遍历,以及而后的自底向上的一次遍历。这可以通过保存一些父链来完成,也可以通过将访问路径存储到栈中来完成。但是,这两种方法均需大量的开销,而且两者都必须处理许多特殊的情况(六种,包括镜像的单旋转、一字型旋转、之字形旋转)。

  在自顶向下的操作是在初始访问路径上施行一些旋转,只用到O(1)的附加空间,但却保持了O(logN)的摊还时间界。伸展树SplayTree的节点的域包括:data, left, right。
  在访问的任意时刻,都有一个当前节点X,它是其子树(即树M)的根;每次访问时,需要考虑将节点Y/Z作为下次迭代时树M的根节点X,然后通过伸展操作将树M中小于Y/Z的节点存储在树L中、将大于Y/Z的节点存储在树R中,除了Y/Z的子树的节点外。其中,Y表示沿单向访问路径的下个节点,Z表示沿单向范文路径的下下个节点,单向访问路径是指访问左子树后继续向左子树访问。初始时X为树T的根,而L和R是空树。此外,还可以发现,树L的根节点的右子节点每次都为NULL,树R的根节点的做自己每次都为NULL。

  假设搜索条件为d时,若满足以下条件,那么迭代结束,且树M的根节点仍为X;

    ① X->data == d

    ② X的左子节点为空

    ③ X的右子节点为空

  否则,当满足以下之一条件时,节点Y将被推为下次迭代时树M的根节点X:

    ① Y为X的左子节点,且Y->data <= d

    ② Y为X的右子节点,且Y->data >= d

    ③ 节点Z为空

  否则,节点Z将被推为下次迭代时树M的根节点X。

  

  上图从上至下分别是单旋转、一字型旋转和之字形旋转的实现。然而,在自顶向下的伸展操作中,之字形旋转操作可以简化成两次单旋转操作。

  若下次迭代时节点Y为树M的根,那么执行单旋转操作,节点Y变成树M的新根,X和子树B作为R中最小项的左儿子附接到R上,X的左儿子变成NULL;

  若下次迭代时节点Z为树M的跟,那么执行一字型旋转操作,在X和Y之间施行一次旋转,把Y和X的子树附接到R上,Y的左儿子变成NULL。

  在迭代结束时们需要处理树L、树M、树R以形成一棵树,姑且可以称之为重聚操作。如下图所示,由于树L的根节点没有右子节点,可以将节点X的左子树挂成树L的根节点的右子树;类似地,将节点X的右子树挂成树R的根节点的左子树。最后将树L和树R分别挂成节点X的左子树和右子树,此时,所有节点重聚成一棵树,根节点为X。

  

伸展的代码实现:

  由于伸展树节点不需要parent域,所以实现左旋、右旋操作的函数需要使用指针引用作为参数。

 1 void rotateWithLeftChild(BinaryTreeNode *&n)
 2 {
 3     BinaryTreeNode *k1 = n->left;
 4     BinaryTreeNode *k2 = n;
 5     k2->left = k1->right;
 6     k1->right = k2;
 7     n = k1;
 8 }
 9     
10 void rotateWithRightChild(BinaryTreeNode *&n)
11 {
12     BinaryTreeNode *k1 = n;
13     BinaryTreeNode *k2 = n->right;
14     k1->right = k2->left;
15     k2->left = k1;
16     n = k2;
17 }

  在伸展操作的函数中,需要额外的O(1)的空间(即header)用于保存树L和树R的根节点信息,其中header.right保存树L的根节点、header.left保存树R的根节点。

  树L还需要一个指示指针leftTreeMax指向树L中的最大项,每当树M中的节点移到树L中均需挂成leftTreeMax节点的右子节点,然后leftTreeMax重指向原leftTreeMax的右子节点,移动后还需要改变树M的根节点,初始时leftTreeMax指向header。此外,当有节点从树M移到树L中时,需要将它的right设置为NULL,以断开树L和树M的关联。

  树R同样需要一个指示指针rightTreeMin做相同的工作。

  迭代结束后,需要重聚操作生成一棵树。

 1 BinaryTreeNode* splay(Comparable d, BinaryTreeNode *midTreeRoot)
 2 {
 3     static BinaryTreeNode header(0);
 4     header.left = header.right = NULL;
 5 
 6     BinaryTreeNode *leftTreeMax, *rightTreeMin;
 7     leftTreeMax = rightTreeMin = &header;
 8 
 9     while (midTreeRoot->data != d)
10     {
11         if (d < midTreeRoot->data)
12         {
13             if (midTreeRoot->left == NULL)
14                 break;
15             if (d < midTreeRoot->left->data && midTreeRoot->left->left)  // zig-zig
16                 rotateWithLeftChild(midTreeRoot);
17             // 右连接
18             rightTreeMin->left = midTreeRoot;
19             rightTreeMin = midTreeRoot;
20             midTreeRoot = midTreeRoot->left;
21             rightTreeMin->left = NULL;
22         }
23         else if (d > midTreeRoot->data)
24         {
25             if (midTreeRoot->right == NULL)
26                 break;
27             if (d > midTreeRoot->right->data && midTreeRoot->right->right)  // zig-zig
28                 rotateWithRightChild(midTreeRoot);
29             // 左连接
30             leftTreeMax->right = midTreeRoot;
31             leftTreeMax = midTreeRoot;
32             midTreeRoot = midTreeRoot->right;
33             leftTreeMax->right = NULL;
34         }
35         else
36             break;
37     }
38 
39     leftTreeMax->right = midTreeRoot->left;
40     rightTreeMin->left = midTreeRoot->right;
41     midTreeRoot->left = header.right;
42     midTreeRoot->right = header.left;
43 
44     return midTreeRoot;
45 }

三 插入节点

  插入节点时需要:

  ① 检测根节点root是否存在,不存在则直接插为根节点root。

  ② 若节点存在,则需进行伸展操作,把离被插入数据最近的节点移到根节点处。

  ③ 若根节点和被插入数据相等,则不插入(数据各异的情况下)。

  ④ 若被插入数据小于根节点数据,则将root的左子树作为新插入节点的左子树,将root及其右子树作为新插入节点的右子树,root重新指向新插入节点。

  ⑤ 若被插入数据大于根节点数据,则将root的右子树作为新插入节点的右子树,将root及其左子树作为新插入节点的左子树,root重新指向新插入节点。

 1 void insert(Comparable d)
 2 {
 3     BinaryTreeNode *newNode = new BinaryTreeNode(d);
 4         
 5     if (root == NULL)
 6         root = newNode;
 7     else
 8     {
 9         root = splay(d, root);
10         if (d < root->data)
11         {
12             newNode->left = root->left;
13             newNode->right = root;
14             root->left = NULL;
15             root = newNode;
16         }
17         else if (d > root->data)
18         {
19             newNode->right = root->right;
20             newNode->left = root;
21             root->right = NULL;
22             root = newNode;
23         }
24         else
25             return;
26     }
27 }

四 删除节点

  删除节点时需要:

  ① 检测根节点是否存在,若不存在则不进行删除操作

  ② 若根节点存在,则进行伸展操作,把离被删除数据最近的节点移到根节点处

  ③ 若根节点和被删除数据相等,且根节点的左子树不存在,则将root重新指向原root的右子节点,并将原root删除。

  ④ 若根节点和被删除数据相等,且根节点的左子树存在,则将root的左子树进行伸展操作,伸展操作后,根据二叉搜索树的性质可知,左子树根节点的右子节点必然为NULL,此时将左子树根节点的右节点指向root的右子节点;并将root重新指向原root左子树根节点,并将原root删除。

 1 void remove(Comparable d)
 2 {
 3     if (root)
 4     {
 5         root = splay(d, root);
 6 
 7         if (d == root->data)
 8         {
 9             BinaryTreeNode *newRoot = NULL;
10 
11             if (!root->left)
12             {
13                 newRoot = root->right;
14             }
15             else
16             {
17                 newRoot = root->left;
18                 newRoot = splay(d, newRoot);
19                 newRoot->right = root->right;
20             }
21             delete root;
22             root = newRoot;
23         }
24     }
25 }

五 完整代码

5.1 SplayTree.hpp

  1 #include <iostream>
  2 
  3 using namespace std;
  4 
  5 //Comparable必须重载 ① '>' ② '<' ③ '==' ④ '<<'
  6 template <typename Comparable>
  7 class SplayTree
  8 {
  9 public:
 10     SplayTree() 
 11     { 
 12         root = NULL;
 13     }
 14 
 15     ~SplayTree() 
 16     { 
 17         makeEmpty(); 
 18     }
 19     
 20     bool isEmpty()
 21     {
 22         return root ? false : true;
 23     }
 24     
 25     void makeEmpty()
 26     {
 27         makeEmpty(root);
 28     }
 29 
 30     bool search(Comparable d)
 31     {
 32         if (!root)
 33             return false;
 34         root = splay(d, root);
 35         if (d == root->data)
 36             return true;
 37         return false;
 38     }
 39 
 40     void insert(Comparable d)
 41     {
 42         BinaryTreeNode *newNode = new BinaryTreeNode(d);
 43         
 44         if (root == NULL)
 45             root = newNode;
 46         else
 47         {
 48             root = splay(d, root);
 49             if (d < root->data)
 50             {
 51                 newNode->left = root->left;
 52                 newNode->right = root;
 53                 root->left = NULL;
 54                 root = newNode;
 55             }
 56             else if (d > root->data)
 57             {
 58                 newNode->right = root->right;
 59                 newNode->left = root;
 60                 root->right = NULL;
 61                 root = newNode;
 62             }
 63             else
 64                 return;
 65         }
 66     }
 67     
 68     void remove(Comparable d)
 69     {
 70         if (root)
 71         {
 72             root = splay(d, root);
 73 
 74             if (d == root->data)
 75             {
 76                 BinaryTreeNode *newRoot = NULL;
 77 
 78                 if (!root->left)
 79                 {
 80                     newRoot = root->right;
 81                 }
 82                 else
 83                 {
 84                     newRoot = root->left;
 85                     newRoot = splay(d, newRoot);
 86                     newRoot->right = root->right;
 87                 }
 88                 delete root;
 89                 root = newRoot;
 90             }
 91         }
 92     }
 93     
 94     void printTree()
 95     {
 96         if (root)
 97         {
 98             printTree(root);
 99             cout << endl;
100         }
101         else
102             cout << "树中没有数据" << endl;
103     }
104 
105 private:
106     struct BinaryTreeNode
107     {
108         Comparable data;
109         BinaryTreeNode *left;
110         BinaryTreeNode *right;
111         
112         BinaryTreeNode(Comparable d, BinaryTreeNode *l=NULL, BinaryTreeNode *r=NULL): data(d), left(l), right(r) {  }
113     };
114 
115     BinaryTreeNode *root;
116     
117     void makeEmpty(BinaryTreeNode *&n) // 需要改变指针参数
118     {
119         if (n)
120         {
121             makeEmpty(n->left);
122             makeEmpty(n->right);
123             delete n;
124             n = NULL;
125         }
126         return;
127     }
128     
129     void printTree(BinaryTreeNode *r)
130     {
131         if (!r)
132             return;
133         printTree(r->left);
134         cout << r->data << " ";
135         printTree(r->right);
136     }
137     
138     void rotateWithLeftChild(BinaryTreeNode *&n)
139     {
140         BinaryTreeNode *k1 = n->left;
141         BinaryTreeNode *k2 = n;
142         k2->left = k1->right;
143         k1->right = k2;
144         n = k1;
145     }
146     
147     void rotateWithRightChild(BinaryTreeNode *&n)
148     {
149         BinaryTreeNode *k1 = n;
150         BinaryTreeNode *k2 = n->right;
151         k1->right = k2->left;
152         k2->left = k1;
153         n = k2;
154     }
155 
156     BinaryTreeNode* splay(Comparable d, BinaryTreeNode *midTreeRoot)
157     {
158         static BinaryTreeNode header(0);
159         header.left = header.right = NULL;
160 
161         BinaryTreeNode *leftTreeMax, *rightTreeMin;
162         leftTreeMax = rightTreeMin = &header;
163         
164         cout << "start splaying:" << endl;
165 
166         while (midTreeRoot->data != d)
167         {
168             if (d < midTreeRoot->data)
169             {
170                 if (midTreeRoot->left == NULL)
171                     break;
172                 if (d < midTreeRoot->left->data && midTreeRoot->left->left)  // zig-zig
173                     rotateWithLeftChild(midTreeRoot);
174                 // 右连接
175                 rightTreeMin->left = midTreeRoot;
176                 rightTreeMin = midTreeRoot;
177                 midTreeRoot = midTreeRoot->left;
178                 rightTreeMin->left = NULL;
179             }
180             else if (d > midTreeRoot->data)
181             {
182                 if (midTreeRoot->right == NULL)
183                     break;
184                 if (d > midTreeRoot->right->data && midTreeRoot->right->right)  // zig-zig
185                     rotateWithRightChild(midTreeRoot);
186                 // 左连接
187                 leftTreeMax->right = midTreeRoot;
188                 leftTreeMax = midTreeRoot;
189                 midTreeRoot = midTreeRoot->right;
190                 leftTreeMax->right = NULL;
191             }
192             else
193                 break;
194         }
195 
196         cout << "before reassembling:" << endl;
197         cout << "L-Tree: ";
198         printTree(header.right);
199         cout << endl;
200         cout << "M-Tree: ";
201         printTree(midTreeRoot);
202         cout << endl;
203         cout << "R-Tree: "; 
204         printTree(header.left);
205         cout << endl;
206 
207         leftTreeMax->right = midTreeRoot->left;
208         rightTreeMin->left = midTreeRoot->right;
209         midTreeRoot->left = header.right;
210         midTreeRoot->right = header.left;
211         
212         cout << "after reassembling:" << endl;
213         cout << "M-Tree: ";
214         printTree(midTreeRoot);
215         cout << endl;
216 
217         return midTreeRoot;
218     }
219 };

5.2 main.cpp

 1 #include <ctime>
 2 #include <iostream>
 3 #include <cstdlib>
 4 
 5 #include "SplayTree.hpp"
 6 
 7 using namespace std;
 8 
 9 const int LENGTH = 9;
10 
11 void generateTree(SplayTree<int> *tree)
12 {
13     srand((unsigned int)time(NULL));
14     int i = LENGTH;
15     while(i--)
16     {
17         tree->insert(rand() % 100);
18         tree->printTree();
19     }
20 }
21 
22 void checkSearch(SplayTree<int> *tree)
23 {
24     int elem;
25     cout << "请输入用来测试搜索的数据: ";
26     cin >> elem;
27     if (tree->search(elem))
28         cout << "存在" << endl;
29     else
30         cout << "不存在" << endl;
31 }
32 
33 void checkRemove(SplayTree<int> *tree)
34 {
35     if (tree->isEmpty())
36     {
37         generateTree(tree);
38         tree->printTree();
39     }
40     while (!tree->isEmpty())
41     {
42         int elem;
43         cout << "请输入要移除的数据: ";
44         cin >> elem;
45         tree->remove(elem);
46         tree->printTree();
47 
48     }
49 }
50 
51 int main()
52 {
53     SplayTree<int> *tree = new SplayTree<int>(); 
54     generateTree(tree);
55     tree->printTree();
56 
57     checkSearch(tree);
58     checkSearch(tree);
59 
60     checkRemove(tree);
61 
62     system("pause");
63 }

5.3 运行结果截图

posted @ 2015-07-15 20:58  yoleimei  阅读(557)  评论(0编辑  收藏  举报