blob: 7b3af8b992a2d11f2b75a5e020b07c498b189564 [file] [log] [blame]
Steve Blocka7e24c12009-10-30 11:49:00 +00001// Copyright 2009 the V8 project authors. All rights reserved.
2// Redistribution and use in source and binary forms, with or without
3// modification, are permitted provided that the following conditions are
4// met:
5//
6// * Redistributions of source code must retain the above copyright
7// notice, this list of conditions and the following disclaimer.
8// * Redistributions in binary form must reproduce the above
9// copyright notice, this list of conditions and the following
10// disclaimer in the documentation and/or other materials provided
11// with the distribution.
12// * Neither the name of Google Inc. nor the names of its
13// contributors may be used to endorse or promote products derived
14// from this software without specific prior written permission.
15//
16// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
20// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
21// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
22// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
26// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27
28
29// A namespace stub. It will become more clear how to declare it properly
30// during integration of this script into Dev Tools.
31var goog = goog || {};
32goog.structs = goog.structs || {};
33
34
35/**
36 * Constructs a Splay tree. A splay tree is a self-balancing binary
37 * search tree with the additional property that recently accessed
38 * elements are quick to access again. It performs basic operations
39 * such as insertion, look-up and removal in O(log(n)) amortized time.
40 *
41 * @constructor
42 */
43goog.structs.SplayTree = function() {
44};
45
46
47/**
48 * Pointer to the root node of the tree.
49 *
50 * @type {goog.structs.SplayTree.Node}
51 * @private
52 */
53goog.structs.SplayTree.prototype.root_ = null;
54
55
56/**
57 * @return {boolean} Whether the tree is empty.
58 */
59goog.structs.SplayTree.prototype.isEmpty = function() {
60 return !this.root_;
61};
62
63
64
65/**
66 * Inserts a node into the tree with the specified key and value if
67 * the tree does not already contain a node with the specified key. If
68 * the value is inserted, it becomes the root of the tree.
69 *
70 * @param {number} key Key to insert into the tree.
71 * @param {*} value Value to insert into the tree.
72 */
73goog.structs.SplayTree.prototype.insert = function(key, value) {
74 if (this.isEmpty()) {
75 this.root_ = new goog.structs.SplayTree.Node(key, value);
76 return;
77 }
78 // Splay on the key to move the last node on the search path for
79 // the key to the root of the tree.
80 this.splay_(key);
81 if (this.root_.key == key) {
82 return;
83 }
84 var node = new goog.structs.SplayTree.Node(key, value);
85 if (key > this.root_.key) {
86 node.left = this.root_;
87 node.right = this.root_.right;
88 this.root_.right = null;
89 } else {
90 node.right = this.root_;
91 node.left = this.root_.left;
92 this.root_.left = null;
93 }
94 this.root_ = node;
95};
96
97
98/**
99 * Removes a node with the specified key from the tree if the tree
100 * contains a node with this key. The removed node is returned. If the
101 * key is not found, an exception is thrown.
102 *
103 * @param {number} key Key to find and remove from the tree.
104 * @return {goog.structs.SplayTree.Node} The removed node.
105 */
106goog.structs.SplayTree.prototype.remove = function(key) {
107 if (this.isEmpty()) {
108 throw Error('Key not found: ' + key);
109 }
110 this.splay_(key);
111 if (this.root_.key != key) {
112 throw Error('Key not found: ' + key);
113 }
114 var removed = this.root_;
115 if (!this.root_.left) {
116 this.root_ = this.root_.right;
117 } else {
118 var right = this.root_.right;
119 this.root_ = this.root_.left;
120 // Splay to make sure that the new root has an empty right child.
121 this.splay_(key);
122 // Insert the original right child as the right child of the new
123 // root.
124 this.root_.right = right;
125 }
126 return removed;
127};
128
129
130/**
131 * Returns the node having the specified key or null if the tree doesn't contain
132 * a node with the specified key.
133 *
134 * @param {number} key Key to find in the tree.
135 * @return {goog.structs.SplayTree.Node} Node having the specified key.
136 */
137goog.structs.SplayTree.prototype.find = function(key) {
138 if (this.isEmpty()) {
139 return null;
140 }
141 this.splay_(key);
142 return this.root_.key == key ? this.root_ : null;
143};
144
145
146/**
147 * @return {goog.structs.SplayTree.Node} Node having the minimum key value.
148 */
149goog.structs.SplayTree.prototype.findMin = function() {
150 if (this.isEmpty()) {
151 return null;
152 }
153 var current = this.root_;
154 while (current.left) {
155 current = current.left;
156 }
157 return current;
158};
159
160
161/**
162 * @return {goog.structs.SplayTree.Node} Node having the maximum key value.
163 */
164goog.structs.SplayTree.prototype.findMax = function(opt_startNode) {
165 if (this.isEmpty()) {
166 return null;
167 }
168 var current = opt_startNode || this.root_;
169 while (current.right) {
170 current = current.right;
171 }
172 return current;
173};
174
175
176/**
177 * @return {goog.structs.SplayTree.Node} Node having the maximum key value that
178 * is less or equal to the specified key value.
179 */
180goog.structs.SplayTree.prototype.findGreatestLessThan = function(key) {
181 if (this.isEmpty()) {
182 return null;
183 }
184 // Splay on the key to move the node with the given key or the last
185 // node on the search path to the top of the tree.
186 this.splay_(key);
187 // Now the result is either the root node or the greatest node in
188 // the left subtree.
189 if (this.root_.key <= key) {
190 return this.root_;
191 } else if (this.root_.left) {
192 return this.findMax(this.root_.left);
193 } else {
194 return null;
195 }
196};
197
198
199/**
200 * @return {Array<*>} An array containing all the values of tree's nodes.
201 */
202goog.structs.SplayTree.prototype.exportValues = function() {
203 var result = [];
204 this.traverse_(function(node) { result.push(node.value); });
205 return result;
206};
207
208
209/**
210 * Perform the splay operation for the given key. Moves the node with
211 * the given key to the top of the tree. If no node has the given
212 * key, the last node on the search path is moved to the top of the
213 * tree. This is the simplified top-down splaying algorithm from:
214 * "Self-adjusting Binary Search Trees" by Sleator and Tarjan
215 *
216 * @param {number} key Key to splay the tree on.
217 * @private
218 */
219goog.structs.SplayTree.prototype.splay_ = function(key) {
220 if (this.isEmpty()) {
221 return;
222 }
223 // Create a dummy node. The use of the dummy node is a bit
224 // counter-intuitive: The right child of the dummy node will hold
225 // the L tree of the algorithm. The left child of the dummy node
226 // will hold the R tree of the algorithm. Using a dummy node, left
227 // and right will always be nodes and we avoid special cases.
228 var dummy, left, right;
229 dummy = left = right = new goog.structs.SplayTree.Node(null, null);
230 var current = this.root_;
231 while (true) {
232 if (key < current.key) {
233 if (!current.left) {
234 break;
235 }
236 if (key < current.left.key) {
237 // Rotate right.
238 var tmp = current.left;
239 current.left = tmp.right;
240 tmp.right = current;
241 current = tmp;
242 if (!current.left) {
243 break;
244 }
245 }
246 // Link right.
247 right.left = current;
248 right = current;
249 current = current.left;
250 } else if (key > current.key) {
251 if (!current.right) {
252 break;
253 }
254 if (key > current.right.key) {
255 // Rotate left.
256 var tmp = current.right;
257 current.right = tmp.left;
258 tmp.left = current;
259 current = tmp;
260 if (!current.right) {
261 break;
262 }
263 }
264 // Link left.
265 left.right = current;
266 left = current;
267 current = current.right;
268 } else {
269 break;
270 }
271 }
272 // Assemble.
273 left.right = current.left;
274 right.left = current.right;
275 current.left = dummy.right;
276 current.right = dummy.left;
277 this.root_ = current;
278};
279
280
281/**
282 * Performs a preorder traversal of the tree.
283 *
284 * @param {function(goog.structs.SplayTree.Node)} f Visitor function.
285 * @private
286 */
287goog.structs.SplayTree.prototype.traverse_ = function(f) {
288 var nodesToVisit = [this.root_];
289 while (nodesToVisit.length > 0) {
290 var node = nodesToVisit.shift();
291 if (node == null) {
292 continue;
293 }
294 f(node);
295 nodesToVisit.push(node.left);
296 nodesToVisit.push(node.right);
297 }
298};
299
300
301/**
302 * Constructs a Splay tree node.
303 *
304 * @param {number} key Key.
305 * @param {*} value Value.
306 */
307goog.structs.SplayTree.Node = function(key, value) {
308 this.key = key;
309 this.value = value;
310};
311
312
313/**
314 * @type {goog.structs.SplayTree.Node}
315 */
316goog.structs.SplayTree.Node.prototype.left = null;
317
318
319/**
320 * @type {goog.structs.SplayTree.Node}
321 */
322goog.structs.SplayTree.Node.prototype.right = null;