• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// 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/**
30 * Constructs a Splay tree.  A splay tree is a self-balancing binary
31 * search tree with the additional property that recently accessed
32 * elements are quick to access again. It performs basic operations
33 * such as insertion, look-up and removal in O(log(n)) amortized time.
34 *
35 * @constructor
36 */
37export class SplayTree {
38
39  /**
40   * Pointer to the root node of the tree.
41   *
42   * @type {SplayTreeNode}
43   * @private
44   */
45  root_ = null;
46
47
48  /**
49   * @return {boolean} Whether the tree is empty.
50   */
51  isEmpty() {
52    return this.root_ === null;
53  }
54
55  /**
56   * Inserts a node into the tree with the specified key and value if
57   * the tree does not already contain a node with the specified key. If
58   * the value is inserted, it becomes the root of the tree.
59   *
60   * @param {number} key Key to insert into the tree.
61   * @param {*} value Value to insert into the tree.
62   */
63  insert(key, value) {
64    if (this.isEmpty()) {
65      this.root_ = new SplayTreeNode(key, value);
66      return;
67    }
68    // Splay on the key to move the last node on the search path for
69    // the key to the root of the tree.
70    this.splay_(key);
71    if (this.root_.key == key) return;
72
73    const node = new SplayTreeNode(key, value);
74    if (key > this.root_.key) {
75      node.left = this.root_;
76      node.right = this.root_.right;
77      this.root_.right = null;
78    } else {
79      node.right = this.root_;
80      node.left = this.root_.left;
81      this.root_.left = null;
82    }
83    this.root_ = node;
84  }
85
86  /**
87   * Removes a node with the specified key from the tree if the tree
88   * contains a node with this key. The removed node is returned. If the
89   * key is not found, an exception is thrown.
90   *
91   * @param {number} key Key to find and remove from the tree.
92   * @return {SplayTreeNode} The removed node.
93   */
94  remove(key) {
95    if (this.isEmpty()) {
96      throw Error(`Key not found: ${key}`);
97    }
98    this.splay_(key);
99    if (this.root_.key != key) {
100      throw Error(`Key not found: ${key}`);
101    }
102    const removed = this.root_;
103    if (this.root_.left === null) {
104      this.root_ = this.root_.right;
105    } else {
106      const { right } = this.root_;
107      this.root_ = this.root_.left;
108      // Splay to make sure that the new root has an empty right child.
109      this.splay_(key);
110      // Insert the original right child as the right child of the new
111      // root.
112      this.root_.right = right;
113    }
114    return removed;
115  }
116
117  /**
118   * Returns the node having the specified key or null if the tree doesn't contain
119   * a node with the specified key.
120   *
121   * @param {number} key Key to find in the tree.
122   * @return {SplayTreeNode} Node having the specified key.
123   */
124  find(key) {
125    if (this.isEmpty()) return null;
126    this.splay_(key);
127    return this.root_.key == key ? this.root_ : null;
128  }
129
130  /**
131   * @return {SplayTreeNode} Node having the minimum key value.
132   */
133  findMin() {
134    if (this.isEmpty()) return null;
135    let current = this.root_;
136    while (current.left !== null) {
137      current = current.left;
138    }
139    return current;
140  }
141
142  /**
143   * @return {SplayTreeNode} Node having the maximum key value.
144   */
145  findMax(opt_startNode) {
146    if (this.isEmpty()) return null;
147    let current = opt_startNode || this.root_;
148    while (current.right !== null) {
149      current = current.right;
150    }
151    return current;
152  }
153
154  /**
155   * @return {SplayTreeNode} Node having the maximum key value that
156   *     is less or equal to the specified key value.
157   */
158  findGreatestLessThan(key) {
159    if (this.isEmpty()) return null;
160    // Splay on the key to move the node with the given key or the last
161    // node on the search path to the top of the tree.
162    this.splay_(key);
163    // Now the result is either the root node or the greatest node in
164    // the left subtree.
165    if (this.root_.key <= key) {
166      return this.root_;
167    } else if (this.root_.left !== null) {
168      return this.findMax(this.root_.left);
169    } else {
170      return null;
171    }
172  }
173
174  /**
175   * @return {Array<*>} An array containing all the values of tree's nodes paired
176   *     with keys.
177   */
178  exportKeysAndValues() {
179    const result = [];
180    this.traverse_(function(node) { result.push([node.key, node.value]); });
181    return result;
182  }
183
184  /**
185   * @return {Array<*>} An array containing all the values of tree's nodes.
186   */
187  exportValues() {
188    const result = [];
189    this.traverse_(function(node) { result.push(node.value) });
190    return result;
191  }
192
193  /**
194   * Perform the splay operation for the given key. Moves the node with
195   * the given key to the top of the tree.  If no node has the given
196   * key, the last node on the search path is moved to the top of the
197   * tree. This is the simplified top-down splaying algorithm from:
198   * "Self-adjusting Binary Search Trees" by Sleator and Tarjan
199   *
200   * @param {number} key Key to splay the tree on.
201   * @private
202   */
203  splay_(key) {
204    if (this.isEmpty()) return;
205    // Create a dummy node.  The use of the dummy node is a bit
206    // counter-intuitive: The right child of the dummy node will hold
207    // the L tree of the algorithm.  The left child of the dummy node
208    // will hold the R tree of the algorithm.  Using a dummy node, left
209    // and right will always be nodes and we avoid special cases.
210    let dummy, left, right;
211    dummy = left = right = new SplayTreeNode(null, null);
212    let current = this.root_;
213    while (true) {
214      if (key < current.key) {
215        if (current.left === null) break;
216        if (key < current.left.key) {
217          // Rotate right.
218          const tmp = current.left;
219          current.left = tmp.right;
220          tmp.right = current;
221          current = tmp;
222          if (current.left === null) break;
223        }
224        // Link right.
225        right.left = current;
226        right = current;
227        current = current.left;
228      } else if (key > current.key) {
229        if (current.right === null) break;
230        if (key > current.right.key) {
231          // Rotate left.
232          const tmp = current.right;
233          current.right = tmp.left;
234          tmp.left = current;
235          current = tmp;
236          if (current.right === null) break;
237        }
238        // Link left.
239        left.right = current;
240        left = current;
241        current = current.right;
242      } else {
243        break;
244      }
245    }
246    // Assemble.
247    left.right = current.left;
248    right.left = current.right;
249    current.left = dummy.right;
250    current.right = dummy.left;
251    this.root_ = current;
252  }
253
254  /**
255   * Performs a preorder traversal of the tree.
256   *
257   * @param {function(SplayTreeNode)} f Visitor function.
258   * @private
259   */
260  traverse_(f) {
261    const nodesToVisit = [this.root_];
262    while (nodesToVisit.length > 0) {
263      const node = nodesToVisit.shift();
264      if (node === null) continue;
265      f(node);
266      nodesToVisit.push(node.left);
267      nodesToVisit.push(node.right);
268    }
269  }
270}
271
272/**
273 * Constructs a Splay tree node.
274 *
275 * @param {number} key Key.
276 * @param {*} value Value.
277 */
278class SplayTreeNode {
279  constructor(key, value) {
280    this.key = key;
281    this.value = value;
282    /**
283     * @type {SplayTreeNode}
284     */
285    this.left = null;
286    /**
287     * @type {SplayTreeNode}
288     */
289    this.right = null;
290  }
291};
292