|
1 /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- |
|
2 * vim: set ts=8 sts=4 et sw=4 tw=99: |
|
3 * This Source Code Form is subject to the terms of the Mozilla Public |
|
4 * License, v. 2.0. If a copy of the MPL was not distributed with this |
|
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */ |
|
6 |
|
7 #ifndef ds_SplayTree_h |
|
8 #define ds_SplayTree_h |
|
9 |
|
10 #include "ds/LifoAlloc.h" |
|
11 |
|
12 namespace js { |
|
13 |
|
14 /* |
|
15 * Class which represents a splay tree with nodes allocated from a LifoAlloc. |
|
16 * Splay trees are balanced binary search trees for which search, insert and |
|
17 * remove are all amortized O(log n). |
|
18 * |
|
19 * T indicates the type of tree elements, C must have a static |
|
20 * compare(const T&, const T&) method ordering the elements. As for LifoAlloc |
|
21 * objects, T objects stored in the tree will not be explicitly destroyed. |
|
22 */ |
|
23 template <class T, class C> |
|
24 class SplayTree |
|
25 { |
|
26 struct Node { |
|
27 T item; |
|
28 Node *left, *right, *parent; |
|
29 |
|
30 Node(const T &item) |
|
31 : item(item), left(nullptr), right(nullptr), parent(nullptr) |
|
32 {} |
|
33 }; |
|
34 |
|
35 LifoAlloc *alloc; |
|
36 Node *root, *freeList; |
|
37 |
|
38 SplayTree(const SplayTree &) MOZ_DELETE; |
|
39 SplayTree &operator=(const SplayTree &) MOZ_DELETE; |
|
40 |
|
41 public: |
|
42 |
|
43 SplayTree(LifoAlloc *alloc = nullptr) |
|
44 : alloc(alloc), root(nullptr), freeList(nullptr) |
|
45 {} |
|
46 |
|
47 void setAllocator(LifoAlloc *alloc) { |
|
48 this->alloc = alloc; |
|
49 } |
|
50 |
|
51 bool empty() const { |
|
52 return !root; |
|
53 } |
|
54 |
|
55 bool contains(const T &v, T *res) |
|
56 { |
|
57 if (!root) |
|
58 return false; |
|
59 Node *last = lookup(v); |
|
60 splay(last); |
|
61 checkCoherency(root, nullptr); |
|
62 if (C::compare(v, last->item) == 0) { |
|
63 *res = last->item; |
|
64 return true; |
|
65 } |
|
66 return false; |
|
67 } |
|
68 |
|
69 bool insert(const T &v) |
|
70 { |
|
71 Node *element = allocateNode(v); |
|
72 if (!element) |
|
73 return false; |
|
74 |
|
75 if (!root) { |
|
76 root = element; |
|
77 return true; |
|
78 } |
|
79 Node *last = lookup(v); |
|
80 int cmp = C::compare(v, last->item); |
|
81 |
|
82 // Don't tolerate duplicate elements. |
|
83 JS_ASSERT(cmp); |
|
84 |
|
85 Node *&parentPointer = (cmp < 0) ? last->left : last->right; |
|
86 JS_ASSERT(!parentPointer); |
|
87 parentPointer = element; |
|
88 element->parent = last; |
|
89 |
|
90 splay(element); |
|
91 checkCoherency(root, nullptr); |
|
92 return true; |
|
93 } |
|
94 |
|
95 void remove(const T &v) |
|
96 { |
|
97 Node *last = lookup(v); |
|
98 JS_ASSERT(last && C::compare(v, last->item) == 0); |
|
99 |
|
100 splay(last); |
|
101 JS_ASSERT(last == root); |
|
102 |
|
103 // Find another node which can be swapped in for the root: either the |
|
104 // rightmost child of the root's left, or the leftmost child of the |
|
105 // root's right. |
|
106 Node *swap, *swapChild; |
|
107 if (root->left) { |
|
108 swap = root->left; |
|
109 while (swap->right) |
|
110 swap = swap->right; |
|
111 swapChild = swap->left; |
|
112 } else if (root->right) { |
|
113 swap = root->right; |
|
114 while (swap->left) |
|
115 swap = swap->left; |
|
116 swapChild = swap->right; |
|
117 } else { |
|
118 freeNode(root); |
|
119 root = nullptr; |
|
120 return; |
|
121 } |
|
122 |
|
123 // The selected node has at most one child, in swapChild. Detach it |
|
124 // from the subtree by replacing it with that child. |
|
125 if (swap == swap->parent->left) |
|
126 swap->parent->left = swapChild; |
|
127 else |
|
128 swap->parent->right = swapChild; |
|
129 if (swapChild) |
|
130 swapChild->parent = swap->parent; |
|
131 |
|
132 root->item = swap->item; |
|
133 freeNode(swap); |
|
134 |
|
135 checkCoherency(root, nullptr); |
|
136 } |
|
137 |
|
138 template <class Op> |
|
139 void forEach(Op op) |
|
140 { |
|
141 forEachInner(op, root); |
|
142 } |
|
143 |
|
144 private: |
|
145 |
|
146 Node *lookup(const T &v) |
|
147 { |
|
148 JS_ASSERT(root); |
|
149 Node *node = root, *parent; |
|
150 do { |
|
151 parent = node; |
|
152 int c = C::compare(v, node->item); |
|
153 if (c == 0) |
|
154 return node; |
|
155 else if (c < 0) |
|
156 node = node->left; |
|
157 else |
|
158 node = node->right; |
|
159 } while (node); |
|
160 return parent; |
|
161 } |
|
162 |
|
163 Node *allocateNode(const T &v) |
|
164 { |
|
165 Node *node = freeList; |
|
166 if (node) { |
|
167 freeList = node->left; |
|
168 new(node) Node(v); |
|
169 return node; |
|
170 } |
|
171 return alloc->new_<Node>(v); |
|
172 } |
|
173 |
|
174 void freeNode(Node *node) |
|
175 { |
|
176 node->left = freeList; |
|
177 freeList = node; |
|
178 } |
|
179 |
|
180 void splay(Node *node) |
|
181 { |
|
182 // Rotate the element until it is at the root of the tree. Performing |
|
183 // the rotations in this fashion preserves the amortized balancing of |
|
184 // the tree. |
|
185 JS_ASSERT(node); |
|
186 while (node != root) { |
|
187 Node *parent = node->parent; |
|
188 if (parent == root) { |
|
189 // Zig rotation. |
|
190 rotate(node); |
|
191 JS_ASSERT(node == root); |
|
192 return; |
|
193 } |
|
194 Node *grandparent = parent->parent; |
|
195 if ((parent->left == node) == (grandparent->left == parent)) { |
|
196 // Zig-zig rotation. |
|
197 rotate(parent); |
|
198 rotate(node); |
|
199 } else { |
|
200 // Zig-zag rotation. |
|
201 rotate(node); |
|
202 rotate(node); |
|
203 } |
|
204 } |
|
205 } |
|
206 |
|
207 void rotate(Node *node) |
|
208 { |
|
209 // Rearrange nodes so that node becomes the parent of its current |
|
210 // parent, while preserving the sortedness of the tree. |
|
211 Node *parent = node->parent; |
|
212 if (parent->left == node) { |
|
213 // x y |
|
214 // y c ==> a x |
|
215 // a b b c |
|
216 parent->left = node->right; |
|
217 if (node->right) |
|
218 node->right->parent = parent; |
|
219 node->right = parent; |
|
220 } else { |
|
221 JS_ASSERT(parent->right == node); |
|
222 // x y |
|
223 // a y ==> x c |
|
224 // b c a b |
|
225 parent->right = node->left; |
|
226 if (node->left) |
|
227 node->left->parent = parent; |
|
228 node->left = parent; |
|
229 } |
|
230 node->parent = parent->parent; |
|
231 parent->parent = node; |
|
232 if (Node *grandparent = node->parent) { |
|
233 if (grandparent->left == parent) |
|
234 grandparent->left = node; |
|
235 else |
|
236 grandparent->right = node; |
|
237 } else { |
|
238 root = node; |
|
239 } |
|
240 } |
|
241 |
|
242 template <class Op> |
|
243 void forEachInner(Op op, Node *node) |
|
244 { |
|
245 if (!node) |
|
246 return; |
|
247 |
|
248 forEachInner(op, node->left); |
|
249 op(node->item); |
|
250 forEachInner(op, node->right); |
|
251 } |
|
252 |
|
253 Node *checkCoherency(Node *node, Node *minimum) |
|
254 { |
|
255 #ifdef DEBUG |
|
256 if (!node) { |
|
257 JS_ASSERT(!root); |
|
258 return nullptr; |
|
259 } |
|
260 JS_ASSERT_IF(!node->parent, node == root); |
|
261 JS_ASSERT_IF(minimum, C::compare(minimum->item, node->item) < 0); |
|
262 if (node->left) { |
|
263 JS_ASSERT(node->left->parent == node); |
|
264 Node *leftMaximum = checkCoherency(node->left, minimum); |
|
265 JS_ASSERT(C::compare(leftMaximum->item, node->item) < 0); |
|
266 } |
|
267 if (node->right) { |
|
268 JS_ASSERT(node->right->parent == node); |
|
269 return checkCoherency(node->right, node); |
|
270 } |
|
271 return node; |
|
272 #else |
|
273 return nullptr; |
|
274 #endif |
|
275 } |
|
276 }; |
|
277 |
|
278 } /* namespace js */ |
|
279 |
|
280 #endif /* ds_SplayTree_h */ |