11cb0ef41Sopenharmony_ci// Copyright 2009 the V8 project authors. All rights reserved. 21cb0ef41Sopenharmony_ci// Redistribution and use in source and binary forms, with or without 31cb0ef41Sopenharmony_ci// modification, are permitted provided that the following conditions are 41cb0ef41Sopenharmony_ci// met: 51cb0ef41Sopenharmony_ci// 61cb0ef41Sopenharmony_ci// * Redistributions of source code must retain the above copyright 71cb0ef41Sopenharmony_ci// notice, this list of conditions and the following disclaimer. 81cb0ef41Sopenharmony_ci// * Redistributions in binary form must reproduce the above 91cb0ef41Sopenharmony_ci// copyright notice, this list of conditions and the following 101cb0ef41Sopenharmony_ci// disclaimer in the documentation and/or other materials provided 111cb0ef41Sopenharmony_ci// with the distribution. 121cb0ef41Sopenharmony_ci// * Neither the name of Google Inc. nor the names of its 131cb0ef41Sopenharmony_ci// contributors may be used to endorse or promote products derived 141cb0ef41Sopenharmony_ci// from this software without specific prior written permission. 151cb0ef41Sopenharmony_ci// 161cb0ef41Sopenharmony_ci// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS 171cb0ef41Sopenharmony_ci// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 181cb0ef41Sopenharmony_ci// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR 191cb0ef41Sopenharmony_ci// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT 201cb0ef41Sopenharmony_ci// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 211cb0ef41Sopenharmony_ci// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT 221cb0ef41Sopenharmony_ci// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, 231cb0ef41Sopenharmony_ci// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY 241cb0ef41Sopenharmony_ci// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 251cb0ef41Sopenharmony_ci// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 261cb0ef41Sopenharmony_ci// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 271cb0ef41Sopenharmony_ci 281cb0ef41Sopenharmony_ci 291cb0ef41Sopenharmony_ci/** 301cb0ef41Sopenharmony_ci * Constructs a Splay tree. A splay tree is a self-balancing binary 311cb0ef41Sopenharmony_ci * search tree with the additional property that recently accessed 321cb0ef41Sopenharmony_ci * elements are quick to access again. It performs basic operations 331cb0ef41Sopenharmony_ci * such as insertion, look-up and removal in O(log(n)) amortized time. 341cb0ef41Sopenharmony_ci * 351cb0ef41Sopenharmony_ci * @constructor 361cb0ef41Sopenharmony_ci */ 371cb0ef41Sopenharmony_ciexport class SplayTree { 381cb0ef41Sopenharmony_ci 391cb0ef41Sopenharmony_ci /** 401cb0ef41Sopenharmony_ci * Pointer to the root node of the tree. 411cb0ef41Sopenharmony_ci * 421cb0ef41Sopenharmony_ci * @type {SplayTreeNode} 431cb0ef41Sopenharmony_ci * @private 441cb0ef41Sopenharmony_ci */ 451cb0ef41Sopenharmony_ci root_ = null; 461cb0ef41Sopenharmony_ci 471cb0ef41Sopenharmony_ci 481cb0ef41Sopenharmony_ci /** 491cb0ef41Sopenharmony_ci * @return {boolean} Whether the tree is empty. 501cb0ef41Sopenharmony_ci */ 511cb0ef41Sopenharmony_ci isEmpty() { 521cb0ef41Sopenharmony_ci return this.root_ === null; 531cb0ef41Sopenharmony_ci } 541cb0ef41Sopenharmony_ci 551cb0ef41Sopenharmony_ci /** 561cb0ef41Sopenharmony_ci * Inserts a node into the tree with the specified key and value if 571cb0ef41Sopenharmony_ci * the tree does not already contain a node with the specified key. If 581cb0ef41Sopenharmony_ci * the value is inserted, it becomes the root of the tree. 591cb0ef41Sopenharmony_ci * 601cb0ef41Sopenharmony_ci * @param {number} key Key to insert into the tree. 611cb0ef41Sopenharmony_ci * @param {*} value Value to insert into the tree. 621cb0ef41Sopenharmony_ci */ 631cb0ef41Sopenharmony_ci insert(key, value) { 641cb0ef41Sopenharmony_ci if (this.isEmpty()) { 651cb0ef41Sopenharmony_ci this.root_ = new SplayTreeNode(key, value); 661cb0ef41Sopenharmony_ci return; 671cb0ef41Sopenharmony_ci } 681cb0ef41Sopenharmony_ci // Splay on the key to move the last node on the search path for 691cb0ef41Sopenharmony_ci // the key to the root of the tree. 701cb0ef41Sopenharmony_ci this.splay_(key); 711cb0ef41Sopenharmony_ci if (this.root_.key == key) return; 721cb0ef41Sopenharmony_ci 731cb0ef41Sopenharmony_ci const node = new SplayTreeNode(key, value); 741cb0ef41Sopenharmony_ci if (key > this.root_.key) { 751cb0ef41Sopenharmony_ci node.left = this.root_; 761cb0ef41Sopenharmony_ci node.right = this.root_.right; 771cb0ef41Sopenharmony_ci this.root_.right = null; 781cb0ef41Sopenharmony_ci } else { 791cb0ef41Sopenharmony_ci node.right = this.root_; 801cb0ef41Sopenharmony_ci node.left = this.root_.left; 811cb0ef41Sopenharmony_ci this.root_.left = null; 821cb0ef41Sopenharmony_ci } 831cb0ef41Sopenharmony_ci this.root_ = node; 841cb0ef41Sopenharmony_ci } 851cb0ef41Sopenharmony_ci 861cb0ef41Sopenharmony_ci /** 871cb0ef41Sopenharmony_ci * Removes a node with the specified key from the tree if the tree 881cb0ef41Sopenharmony_ci * contains a node with this key. The removed node is returned. If the 891cb0ef41Sopenharmony_ci * key is not found, an exception is thrown. 901cb0ef41Sopenharmony_ci * 911cb0ef41Sopenharmony_ci * @param {number} key Key to find and remove from the tree. 921cb0ef41Sopenharmony_ci * @return {SplayTreeNode} The removed node. 931cb0ef41Sopenharmony_ci */ 941cb0ef41Sopenharmony_ci remove(key) { 951cb0ef41Sopenharmony_ci if (this.isEmpty()) { 961cb0ef41Sopenharmony_ci throw Error(`Key not found: ${key}`); 971cb0ef41Sopenharmony_ci } 981cb0ef41Sopenharmony_ci this.splay_(key); 991cb0ef41Sopenharmony_ci if (this.root_.key != key) { 1001cb0ef41Sopenharmony_ci throw Error(`Key not found: ${key}`); 1011cb0ef41Sopenharmony_ci } 1021cb0ef41Sopenharmony_ci const removed = this.root_; 1031cb0ef41Sopenharmony_ci if (this.root_.left === null) { 1041cb0ef41Sopenharmony_ci this.root_ = this.root_.right; 1051cb0ef41Sopenharmony_ci } else { 1061cb0ef41Sopenharmony_ci const { right } = this.root_; 1071cb0ef41Sopenharmony_ci this.root_ = this.root_.left; 1081cb0ef41Sopenharmony_ci // Splay to make sure that the new root has an empty right child. 1091cb0ef41Sopenharmony_ci this.splay_(key); 1101cb0ef41Sopenharmony_ci // Insert the original right child as the right child of the new 1111cb0ef41Sopenharmony_ci // root. 1121cb0ef41Sopenharmony_ci this.root_.right = right; 1131cb0ef41Sopenharmony_ci } 1141cb0ef41Sopenharmony_ci return removed; 1151cb0ef41Sopenharmony_ci } 1161cb0ef41Sopenharmony_ci 1171cb0ef41Sopenharmony_ci /** 1181cb0ef41Sopenharmony_ci * Returns the node having the specified key or null if the tree doesn't contain 1191cb0ef41Sopenharmony_ci * a node with the specified key. 1201cb0ef41Sopenharmony_ci * 1211cb0ef41Sopenharmony_ci * @param {number} key Key to find in the tree. 1221cb0ef41Sopenharmony_ci * @return {SplayTreeNode} Node having the specified key. 1231cb0ef41Sopenharmony_ci */ 1241cb0ef41Sopenharmony_ci find(key) { 1251cb0ef41Sopenharmony_ci if (this.isEmpty()) return null; 1261cb0ef41Sopenharmony_ci this.splay_(key); 1271cb0ef41Sopenharmony_ci return this.root_.key == key ? this.root_ : null; 1281cb0ef41Sopenharmony_ci } 1291cb0ef41Sopenharmony_ci 1301cb0ef41Sopenharmony_ci /** 1311cb0ef41Sopenharmony_ci * @return {SplayTreeNode} Node having the minimum key value. 1321cb0ef41Sopenharmony_ci */ 1331cb0ef41Sopenharmony_ci findMin() { 1341cb0ef41Sopenharmony_ci if (this.isEmpty()) return null; 1351cb0ef41Sopenharmony_ci let current = this.root_; 1361cb0ef41Sopenharmony_ci while (current.left !== null) { 1371cb0ef41Sopenharmony_ci current = current.left; 1381cb0ef41Sopenharmony_ci } 1391cb0ef41Sopenharmony_ci return current; 1401cb0ef41Sopenharmony_ci } 1411cb0ef41Sopenharmony_ci 1421cb0ef41Sopenharmony_ci /** 1431cb0ef41Sopenharmony_ci * @return {SplayTreeNode} Node having the maximum key value. 1441cb0ef41Sopenharmony_ci */ 1451cb0ef41Sopenharmony_ci findMax(opt_startNode) { 1461cb0ef41Sopenharmony_ci if (this.isEmpty()) return null; 1471cb0ef41Sopenharmony_ci let current = opt_startNode || this.root_; 1481cb0ef41Sopenharmony_ci while (current.right !== null) { 1491cb0ef41Sopenharmony_ci current = current.right; 1501cb0ef41Sopenharmony_ci } 1511cb0ef41Sopenharmony_ci return current; 1521cb0ef41Sopenharmony_ci } 1531cb0ef41Sopenharmony_ci 1541cb0ef41Sopenharmony_ci /** 1551cb0ef41Sopenharmony_ci * @return {SplayTreeNode} Node having the maximum key value that 1561cb0ef41Sopenharmony_ci * is less or equal to the specified key value. 1571cb0ef41Sopenharmony_ci */ 1581cb0ef41Sopenharmony_ci findGreatestLessThan(key) { 1591cb0ef41Sopenharmony_ci if (this.isEmpty()) return null; 1601cb0ef41Sopenharmony_ci // Splay on the key to move the node with the given key or the last 1611cb0ef41Sopenharmony_ci // node on the search path to the top of the tree. 1621cb0ef41Sopenharmony_ci this.splay_(key); 1631cb0ef41Sopenharmony_ci // Now the result is either the root node or the greatest node in 1641cb0ef41Sopenharmony_ci // the left subtree. 1651cb0ef41Sopenharmony_ci if (this.root_.key <= key) { 1661cb0ef41Sopenharmony_ci return this.root_; 1671cb0ef41Sopenharmony_ci } else if (this.root_.left !== null) { 1681cb0ef41Sopenharmony_ci return this.findMax(this.root_.left); 1691cb0ef41Sopenharmony_ci } else { 1701cb0ef41Sopenharmony_ci return null; 1711cb0ef41Sopenharmony_ci } 1721cb0ef41Sopenharmony_ci } 1731cb0ef41Sopenharmony_ci 1741cb0ef41Sopenharmony_ci /** 1751cb0ef41Sopenharmony_ci * @return {Array<*>} An array containing all the values of tree's nodes paired 1761cb0ef41Sopenharmony_ci * with keys. 1771cb0ef41Sopenharmony_ci */ 1781cb0ef41Sopenharmony_ci exportKeysAndValues() { 1791cb0ef41Sopenharmony_ci const result = []; 1801cb0ef41Sopenharmony_ci this.traverse_(function(node) { result.push([node.key, node.value]); }); 1811cb0ef41Sopenharmony_ci return result; 1821cb0ef41Sopenharmony_ci } 1831cb0ef41Sopenharmony_ci 1841cb0ef41Sopenharmony_ci /** 1851cb0ef41Sopenharmony_ci * @return {Array<*>} An array containing all the values of tree's nodes. 1861cb0ef41Sopenharmony_ci */ 1871cb0ef41Sopenharmony_ci exportValues() { 1881cb0ef41Sopenharmony_ci const result = []; 1891cb0ef41Sopenharmony_ci this.traverse_(function(node) { result.push(node.value) }); 1901cb0ef41Sopenharmony_ci return result; 1911cb0ef41Sopenharmony_ci } 1921cb0ef41Sopenharmony_ci 1931cb0ef41Sopenharmony_ci /** 1941cb0ef41Sopenharmony_ci * Perform the splay operation for the given key. Moves the node with 1951cb0ef41Sopenharmony_ci * the given key to the top of the tree. If no node has the given 1961cb0ef41Sopenharmony_ci * key, the last node on the search path is moved to the top of the 1971cb0ef41Sopenharmony_ci * tree. This is the simplified top-down splaying algorithm from: 1981cb0ef41Sopenharmony_ci * "Self-adjusting Binary Search Trees" by Sleator and Tarjan 1991cb0ef41Sopenharmony_ci * 2001cb0ef41Sopenharmony_ci * @param {number} key Key to splay the tree on. 2011cb0ef41Sopenharmony_ci * @private 2021cb0ef41Sopenharmony_ci */ 2031cb0ef41Sopenharmony_ci splay_(key) { 2041cb0ef41Sopenharmony_ci if (this.isEmpty()) return; 2051cb0ef41Sopenharmony_ci // Create a dummy node. The use of the dummy node is a bit 2061cb0ef41Sopenharmony_ci // counter-intuitive: The right child of the dummy node will hold 2071cb0ef41Sopenharmony_ci // the L tree of the algorithm. The left child of the dummy node 2081cb0ef41Sopenharmony_ci // will hold the R tree of the algorithm. Using a dummy node, left 2091cb0ef41Sopenharmony_ci // and right will always be nodes and we avoid special cases. 2101cb0ef41Sopenharmony_ci let dummy, left, right; 2111cb0ef41Sopenharmony_ci dummy = left = right = new SplayTreeNode(null, null); 2121cb0ef41Sopenharmony_ci let current = this.root_; 2131cb0ef41Sopenharmony_ci while (true) { 2141cb0ef41Sopenharmony_ci if (key < current.key) { 2151cb0ef41Sopenharmony_ci if (current.left === null) break; 2161cb0ef41Sopenharmony_ci if (key < current.left.key) { 2171cb0ef41Sopenharmony_ci // Rotate right. 2181cb0ef41Sopenharmony_ci const tmp = current.left; 2191cb0ef41Sopenharmony_ci current.left = tmp.right; 2201cb0ef41Sopenharmony_ci tmp.right = current; 2211cb0ef41Sopenharmony_ci current = tmp; 2221cb0ef41Sopenharmony_ci if (current.left === null) break; 2231cb0ef41Sopenharmony_ci } 2241cb0ef41Sopenharmony_ci // Link right. 2251cb0ef41Sopenharmony_ci right.left = current; 2261cb0ef41Sopenharmony_ci right = current; 2271cb0ef41Sopenharmony_ci current = current.left; 2281cb0ef41Sopenharmony_ci } else if (key > current.key) { 2291cb0ef41Sopenharmony_ci if (current.right === null) break; 2301cb0ef41Sopenharmony_ci if (key > current.right.key) { 2311cb0ef41Sopenharmony_ci // Rotate left. 2321cb0ef41Sopenharmony_ci const tmp = current.right; 2331cb0ef41Sopenharmony_ci current.right = tmp.left; 2341cb0ef41Sopenharmony_ci tmp.left = current; 2351cb0ef41Sopenharmony_ci current = tmp; 2361cb0ef41Sopenharmony_ci if (current.right === null) break; 2371cb0ef41Sopenharmony_ci } 2381cb0ef41Sopenharmony_ci // Link left. 2391cb0ef41Sopenharmony_ci left.right = current; 2401cb0ef41Sopenharmony_ci left = current; 2411cb0ef41Sopenharmony_ci current = current.right; 2421cb0ef41Sopenharmony_ci } else { 2431cb0ef41Sopenharmony_ci break; 2441cb0ef41Sopenharmony_ci } 2451cb0ef41Sopenharmony_ci } 2461cb0ef41Sopenharmony_ci // Assemble. 2471cb0ef41Sopenharmony_ci left.right = current.left; 2481cb0ef41Sopenharmony_ci right.left = current.right; 2491cb0ef41Sopenharmony_ci current.left = dummy.right; 2501cb0ef41Sopenharmony_ci current.right = dummy.left; 2511cb0ef41Sopenharmony_ci this.root_ = current; 2521cb0ef41Sopenharmony_ci } 2531cb0ef41Sopenharmony_ci 2541cb0ef41Sopenharmony_ci /** 2551cb0ef41Sopenharmony_ci * Performs a preorder traversal of the tree. 2561cb0ef41Sopenharmony_ci * 2571cb0ef41Sopenharmony_ci * @param {function(SplayTreeNode)} f Visitor function. 2581cb0ef41Sopenharmony_ci * @private 2591cb0ef41Sopenharmony_ci */ 2601cb0ef41Sopenharmony_ci traverse_(f) { 2611cb0ef41Sopenharmony_ci const nodesToVisit = [this.root_]; 2621cb0ef41Sopenharmony_ci while (nodesToVisit.length > 0) { 2631cb0ef41Sopenharmony_ci const node = nodesToVisit.shift(); 2641cb0ef41Sopenharmony_ci if (node === null) continue; 2651cb0ef41Sopenharmony_ci f(node); 2661cb0ef41Sopenharmony_ci nodesToVisit.push(node.left); 2671cb0ef41Sopenharmony_ci nodesToVisit.push(node.right); 2681cb0ef41Sopenharmony_ci } 2691cb0ef41Sopenharmony_ci } 2701cb0ef41Sopenharmony_ci} 2711cb0ef41Sopenharmony_ci 2721cb0ef41Sopenharmony_ci/** 2731cb0ef41Sopenharmony_ci * Constructs a Splay tree node. 2741cb0ef41Sopenharmony_ci * 2751cb0ef41Sopenharmony_ci * @param {number} key Key. 2761cb0ef41Sopenharmony_ci * @param {*} value Value. 2771cb0ef41Sopenharmony_ci */ 2781cb0ef41Sopenharmony_ciclass SplayTreeNode { 2791cb0ef41Sopenharmony_ci constructor(key, value) { 2801cb0ef41Sopenharmony_ci this.key = key; 2811cb0ef41Sopenharmony_ci this.value = value; 2821cb0ef41Sopenharmony_ci /** 2831cb0ef41Sopenharmony_ci * @type {SplayTreeNode} 2841cb0ef41Sopenharmony_ci */ 2851cb0ef41Sopenharmony_ci this.left = null; 2861cb0ef41Sopenharmony_ci /** 2871cb0ef41Sopenharmony_ci * @type {SplayTreeNode} 2881cb0ef41Sopenharmony_ci */ 2891cb0ef41Sopenharmony_ci this.right = null; 2901cb0ef41Sopenharmony_ci } 2911cb0ef41Sopenharmony_ci}; 292