splaytree.js 9.0 KB

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