splaytree.mjs 8.65 KB
Newer Older
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36
// Copyright 2009 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//     * Redistributions of source code must retain the above copyright
//       notice, this list of conditions and the following disclaimer.
//     * Redistributions in binary form must reproduce the above
//       copyright notice, this list of conditions and the following
//       disclaimer in the documentation and/or other materials provided
//       with the distribution.
//     * Neither the name of Google Inc. nor the names of its
//       contributors may be used to endorse or promote products derived
//       from this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.


/**
 * Constructs a Splay tree.  A splay tree is a self-balancing binary
 * search tree with the additional property that recently accessed
 * elements are quick to access again. It performs basic operations
 * such as insertion, look-up and removal in O(log(n)) amortized time.
 *
 * @constructor
 */
37 38 39 40 41 42 43 44 45 46 47 48 49 50 51
export class SplayTree {

  /**
   * Pointer to the root node of the tree.
   *
   * @type {SplayTreeNode}
   * @private
   */
  root_ = null;


  /**
   * @return {boolean} Whether the tree is empty.
   */
  isEmpty() {
52
    return this.root_ === null;
53 54
  }

55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71
  /**
   * Inserts a node into the tree with the specified key and value if
   * the tree does not already contain a node with the specified key. If
   * the value is inserted, it becomes the root of the tree.
   *
   * @param {number} key Key to insert into the tree.
   * @param {*} value Value to insert into the tree.
   */
  insert(key, value) {
    if (this.isEmpty()) {
      this.root_ = new SplayTreeNode(key, value);
      return;
    }
    // Splay on the key to move the last node on the search path for
    // the key to the root of the tree.
    this.splay_(key);
    if (this.root_.key == key) return;
72

73 74 75 76 77 78 79 80 81 82 83
    const node = new SplayTreeNode(key, value);
    if (key > this.root_.key) {
      node.left = this.root_;
      node.right = this.root_.right;
      this.root_.right = null;
    } else {
      node.right = this.root_;
      node.left = this.root_.left;
      this.root_.left = null;
    }
    this.root_ = node;
84
  }
85 86 87 88 89 90 91 92 93 94 95 96 97

  /**
   * Removes a node with the specified key from the tree if the tree
   * contains a node with this key. The removed node is returned. If the
   * key is not found, an exception is thrown.
   *
   * @param {number} key Key to find and remove from the tree.
   * @return {SplayTreeNode} The removed node.
   */
  remove(key) {
    if (this.isEmpty()) {
      throw Error(`Key not found: ${key}`);
    }
98
    this.splay_(key);
99 100 101 102
    if (this.root_.key != key) {
      throw Error(`Key not found: ${key}`);
    }
    const removed = this.root_;
103
    if (this.root_.left === null) {
104 105 106 107 108 109 110 111 112 113 114
      this.root_ = this.root_.right;
    } else {
      const { right } = this.root_;
      this.root_ = this.root_.left;
      // Splay to make sure that the new root has an empty right child.
      this.splay_(key);
      // Insert the original right child as the right child of the new
      // root.
      this.root_.right = right;
    }
    return removed;
115 116
  }

117 118 119 120 121 122 123 124 125 126 127
  /**
   * Returns the node having the specified key or null if the tree doesn't contain
   * a node with the specified key.
   *
   * @param {number} key Key to find in the tree.
   * @return {SplayTreeNode} Node having the specified key.
   */
  find(key) {
    if (this.isEmpty()) return null;
    this.splay_(key);
    return this.root_.key == key ? this.root_ : null;
128 129
  }

130 131 132 133 134 135
  /**
   * @return {SplayTreeNode} Node having the minimum key value.
   */
  findMin() {
    if (this.isEmpty()) return null;
    let current = this.root_;
136
    while (current.left !== null) {
137 138 139
      current = current.left;
    }
    return current;
140 141
  }

142 143 144 145 146 147
  /**
   * @return {SplayTreeNode} Node having the maximum key value.
   */
  findMax(opt_startNode) {
    if (this.isEmpty()) return null;
    let current = opt_startNode || this.root_;
148
    while (current.right !== null) {
149 150 151
      current = current.right;
    }
    return current;
152 153
  }

154 155 156 157 158 159 160 161 162 163 164 165 166
  /**
   * @return {SplayTreeNode} Node having the maximum key value that
   *     is less or equal to the specified key value.
   */
  findGreatestLessThan(key) {
    if (this.isEmpty()) return null;
    // Splay on the key to move the node with the given key or the last
    // node on the search path to the top of the tree.
    this.splay_(key);
    // Now the result is either the root node or the greatest node in
    // the left subtree.
    if (this.root_.key <= key) {
      return this.root_;
167
    } else if (this.root_.left !== null) {
168 169 170 171
      return this.findMax(this.root_.left);
    } else {
      return null;
    }
172 173
  }

174 175 176 177 178 179 180 181 182
  /**
   * @return {Array<*>} An array containing all the values of tree's nodes paired
   *     with keys.
   */
  exportKeysAndValues() {
    const result = [];
    this.traverse_(function(node) { result.push([node.key, node.value]); });
    return result;
  }
183

184 185 186 187 188
  /**
   * @return {Array<*>} An array containing all the values of tree's nodes.
   */
  exportValues() {
    const result = [];
189
    this.traverse_(function(node) { result.push(node.value) });
190
    return result;
191
  }
192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214

  /**
   * Perform the splay operation for the given key. Moves the node with
   * the given key to the top of the tree.  If no node has the given
   * key, the last node on the search path is moved to the top of the
   * tree. This is the simplified top-down splaying algorithm from:
   * "Self-adjusting Binary Search Trees" by Sleator and Tarjan
   *
   * @param {number} key Key to splay the tree on.
   * @private
   */
  splay_(key) {
    if (this.isEmpty()) return;
    // Create a dummy node.  The use of the dummy node is a bit
    // counter-intuitive: The right child of the dummy node will hold
    // the L tree of the algorithm.  The left child of the dummy node
    // will hold the R tree of the algorithm.  Using a dummy node, left
    // and right will always be nodes and we avoid special cases.
    let dummy, left, right;
    dummy = left = right = new SplayTreeNode(null, null);
    let current = this.root_;
    while (true) {
      if (key < current.key) {
215
        if (current.left === null) break;
216 217 218 219 220 221
        if (key < current.left.key) {
          // Rotate right.
          const tmp = current.left;
          current.left = tmp.right;
          tmp.right = current;
          current = tmp;
222
          if (current.left === null) break;
223 224 225 226 227 228
        }
        // Link right.
        right.left = current;
        right = current;
        current = current.left;
      } else if (key > current.key) {
229
        if (current.right === null) break;
230 231 232 233 234 235
        if (key > current.right.key) {
          // Rotate left.
          const tmp = current.right;
          current.right = tmp.left;
          tmp.left = current;
          current = tmp;
236
          if (current.right === null) break;
237 238 239 240 241 242 243
        }
        // Link left.
        left.right = current;
        left = current;
        current = current.right;
      } else {
        break;
244 245
      }
    }
246 247 248 249 250 251
    // Assemble.
    left.right = current.left;
    right.left = current.right;
    current.left = dummy.right;
    current.right = dummy.left;
    this.root_ = current;
252 253
  }

254 255 256 257 258 259 260 261 262 263
  /**
   * Performs a preorder traversal of the tree.
   *
   * @param {function(SplayTreeNode)} f Visitor function.
   * @private
   */
  traverse_(f) {
    const nodesToVisit = [this.root_];
    while (nodesToVisit.length > 0) {
      const node = nodesToVisit.shift();
264
      if (node === null) continue;
265 266 267
      f(node);
      nodesToVisit.push(node.left);
      nodesToVisit.push(node.right);
268 269
    }
  }
270
}
271 272 273 274 275 276 277

/**
 * Constructs a Splay tree node.
 *
 * @param {number} key Key.
 * @param {*} value Value.
 */
278 279 280 281 282 283 284 285 286 287 288 289 290
class SplayTreeNode {
  constructor(key, value) {
    this.key = key;
    this.value = value;
    /**
     * @type {SplayTreeNode}
     */
    this.left = null;
    /**
     * @type {SplayTreeNode}
     */
    this.right = null;
  }
291
};