├── README.md ├── algorithm.js ├── package.json └── tests.js /README.md: -------------------------------------------------------------------------------- 1 | ## Data Structures & Algorithms for Javascript 2 | 3 | var algo = require('algorithm'); 4 | 5 | 6 | ### Data Structures: 7 | 1. Queue/FIFO Operations & Properties: 8 | * (constructor) pushes every argument that is passed into the queue 9 | * push(1, 2, 3, 4): Pushes 4 integers into the queue - O(1) 10 | * pop(): Removes the earliest value from the queue and returns it - O(1) 11 | * top: Returns the earliest pushed value without removing it - O(1) 12 | * length: Returns the number of elements in the queue 13 | 14 | var q = new algo.Queue(1, 2, 3, 4); 15 | 16 | 17 | 2. Stack/FILO/LIFO Operations & Properties: 18 | * (constructor) pushes every argument that is passed into the stack 19 | * push(1, 2, 3, 4) - O(1) 20 | * pop() - O(1) 21 | * top - O(1) 22 | * Indexing (like an array) - O(1) 23 | * length: Returns the number of elements in the stack 24 | 25 | 3. MinHeap Operations & Properties: 26 | * (constructor) takes in an (possibly non-empty) array which will be used for storage 27 | * push/insert(1, 2, 3, 4): Pushes 4 integers into the heap - O(log n) 28 | * pop(): Removes the smallest value from the heap and returns it - O(log n) 29 | * top: Returns the smallest value in the heap without removing it - O(1) 30 | * length: Returns the number of elements in the heap 31 | 32 | 4. Similarly, we have MaxHeap as well. 33 | 34 | 5. There is also a general Heap/PriorityQueue that can be constructed using 35 | a comparator and an existing array: 36 | 37 | var h = new algo.PriorityQueue(algo.cmp_lt, [92, 19, 192, 11, 0, 3]) 38 | 39 | 6. MinMaxHeap/PriorityDequeue Operations & Properties: 40 | * (constructor) takes in a less-than comparator and an (possibly non-empty) array 41 | which will be used for storage 42 | * push/insert(1, 2, 3, 4): Pushes 4 integers into the heap - O(log n) 43 | * pop_min(): Removes the smallest value from the heap and returns it - O(log n) 44 | * pop_max(): Removes the largest value from the heap and returns it - O(log n) 45 | * min: Returns the smallest value in the heap without removing it - O(1) 46 | * max: Returns the largest value in the heap without removing it - O(1) 47 | * length: Returns the number of elements in the heap 48 | 49 | var mmh = new algo.MinMaxHeap(algo.cmp_lt, [45, 2, 54, 12, 21, 99, 1]); 50 | 51 | 7. Trie Operations & Properties: 52 | * insert('str1', 'str2', 'str3'): Pushes 3 strings into the Trie 53 | * remove('str2'): Removes 'str2' from the Trie. Retrns TRUE if 'str2' was 54 | removed, and FALSE otherwise 55 | * remove_many('str1', 'str2', 'str4'): Removes 3 strings from the Trie. Retruns 56 | the number of items actually removed 57 | * exists('str4'): Retutns TRUE or FALSE depending upon whether 'str4' exists 58 | in the trie or not. 59 | * forEach(callback): Iterates through every element of the Trie in lexicographically 60 | non-increasing order. The callback gets 2 parameters: The value and the index in 61 | the lexicographic order of the traversal. (Check the tests.js file for an example 62 | of this in action) 63 | * length: Returns the number of elements in the Trie 64 | 65 | A Trie is like a set. Adding an element multiple times does not increase the 66 | length of the Trie by more than 1. 67 | 68 | 8. Disjoint Set Operations & Properties: 69 | * constructor(value): Create a DisjointSet object with a single element 'value' 70 | * representative: Returns the representative Set for the current Set 71 | * union: Meld 2 DisjointSet objects into one so that they have the same 72 | representative Set 73 | * length: Returns the number of elements that the representative Set of this 74 | Set has under it 75 | * You can find more information about the Disjoint Set Data Structure on these pages: 76 | * https://secure.wikimedia.org/wikipedia/en/wiki/Disjoint-set_data_structure 77 | * http://www.topcoder.com/tc?module=Static&d1=tutorials&d2=disjointDataStructure 78 | 79 | 9. AVL Tree Operations & Properties: 80 | * constructor(cmp_lt, hook0, hook1, hook2, ...): The 1st argument is a < comparator. 81 | All subsequent arguments are "hook" functions that are called when the tree is 82 | rebalanced so that user-level metadata can be updated. See the function test_avl_tree_hooks() 83 | in the file 'tests.js' for an example on how to use hook functions. 84 | * insert(value): O(log n) 85 | * remove(value): O(log n) 86 | * find(value): O(log n) 87 | * successor(node): Locate the successor of 'node'. The successor of a node is the smallest 88 | node in the Tree that is greater than the current node. O(log n) 89 | * predecessor(node): Locate the predecessor of 'node'. The predecessor of a node is the greatest 90 | node in the Tree that is smallest than the current node. O(log n) 91 | * lower_bound(value): Locate the *first* node before which 'value' can safely be inserted. O(log n) 92 | * upper_bound(value): Locate the *last* node before which 'value' can safely be inserted. O(log n) 93 | * find_by_rank(k): Locate the k'th smallest element in the Tree. 1 <= k <= Tree.length. 94 | O(log n) 95 | * forEach(proc): Iterate over every element in the tree in sorted order 96 | (in-order traversal). O(n) 97 | * toGraphviz(): Return a string that can be fed to the Graphviz tool to display 98 | the AVL Tree as it currently looks. O(n) 99 | * min: O(log n) 100 | * max: O(log n) 101 | * length: The total number of elements in the Tree. O(1) 102 | * height: The length of the longest path from root to leaf. O(1) 103 | * clear: Empty the Tree. O(1) 104 | * This AVL Tree can store multiple elements with the *same key* 105 | * You can find more information about the AVL Tree Data Structure on these pages: 106 | * http://en.wikipedia.org/wiki/AVL_tree 107 | * http://en.wikipedia.org/wiki/Tree_rotation 108 | * http://closure-library.googlecode.com/svn/docs/closure_goog_structs_avltree.js.source.html 109 | 110 | 111 | ### Algorithms: 112 | 1. range(range/array, start index, one after end index): Retuns a range of 113 | values from the passed array. The returned range is also an array. O(n) 114 | 115 | 2. lower_bound(range, value, cmp_lt): (range MUST be sorted) Returns the first 116 | location before which 'value' can safely be inserted so that the resulting range is also 117 | sorted. Will return one past the last valid index if value is greater than any 118 | element in the list. O(log n) 119 | 120 | 3. upper_bound(range, value, cmp_lt): (range MUST be sorted) Returns the last 121 | location before which 'value' can safely be inserted so that the resulting range is also 122 | sorted. Will return one past the last valid index if value is greater than any 123 | element in the list. O(log n) 124 | 125 | 4. equal_range(range, value, cmp_lt): (range MUST be sorted) Returns the first and 126 | last locations before and after which 'value' can safely be inserted so that the 127 | resulting range is also sorted. O(log n) 128 | 129 | 5. binary_search(range, value, cmp_lt): (range MUST be sorted) Returns the first 130 | index where value is equal to the value at index. Returns -1 if the value is not to 131 | be found in the range. O(log n) 132 | 133 | 6. partition(range, pivot_index, cmp_lt): Partitions a range around the element at 134 | range[pivot_index] and returns the index in the modified range that corresponds 135 | to the location before which pivot can be inserted so that the partition remains 136 | valid. 137 | Time Complexity: O(n) 138 | Space Complexity: O(1) 139 | 140 | 7. stable_partition: Same as above, but retains the original order of elements for 141 | elements that compare equal. 142 | Time Complexity: O(n) 143 | Space Complexity: O(n) 144 | 145 | 8. merge(range1, range2, cmp_lt): Merges 2 sorted ranges and returns a new merged 146 | range. 147 | Time Complexity: O(n) 148 | Space Complexity: O(n) 149 | 150 | 9. is_sorted(range, cmp_lt): Returns true or false depending on whether range 151 | is sorted according to 'cmp_lt'. 152 | 153 | 10. is_heap(range, cmp_lt): Returns true or false depending on whether range 154 | is a heap according to 'cmp_lt'. If 'cmp_gt' is used, then is_heap will check 155 | range for being in Max-Heap order. If 'cmp_lt' is used, it will check for 156 | range to be in Min-Heap order. 157 | 158 | 11. heap_sort(input, cmp): Sorts 'input' using comparator 'cmp'. Sorts the 159 | array 'input' in-place. Returns the sorted array. The array passed as 'input' 160 | WILL be modified. This is an unstable sort - O(n log n) 161 | 162 | 12. randomized_select(range, k, cmp): Select the k'th smallest element from 163 | 'range' using 'cmp' as the less-than comparator. The expected runtime 164 | complexity of this function is O(n). 165 | 166 | 167 | ### Comparators: 168 | All Comparators return either true or false only. 169 | 170 | 1. cmp_lt(lhs, rhs): Returns whatever lhs < rhs returns 171 | 172 | 2. cmp_gt(lhs, rhs): Uses < to do a > comparison 173 | 174 | 3. cmp_lt_eq(lhs, rhs): Uses < to do a <= comparison 175 | 176 | 4. cmp_gt_eq(lhs, rhs): Uses < to do a >= comparison 177 | 178 | 5. cmp_eq(lhs, rhs): Uses < to do an == comparison 179 | 180 | 6. cmp_gt_gen(cmp_lt): Given a less-than comparator, generates a greater-than 181 | comparator from it 182 | 183 | 7. cmp_gt_eq_gen(cmp_lt): Given a less-than comparator, generates a greater-than 184 | or equal to comparator from it 185 | 186 | 8. cmp_lt_eq_gen(cmp_lt): Given a less-than comparator, generates a less-than 187 | or equal to comparator from it 188 | 189 | 9. cmp_eq_gen(cmp_lt): Given a less-than comparator, generates an equal to 190 | comparator from it 191 | -------------------------------------------------------------------------------- /algorithm.js: -------------------------------------------------------------------------------- 1 | // -*- tab-width:4 -*- 2 | 3 | /* 4 | * Copyright (c) 2011 Dhruv Matani 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy 7 | * of this software and associated documentation files (the "Software"), to deal 8 | * in the Software without restriction, including without limitation the rights 9 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | * copies of the Software, and to permit persons to whom the Software is 11 | * furnished to do so, subject to the following conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be included in 14 | * all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 22 | * THE SOFTWARE. 23 | * 24 | */ 25 | 26 | 27 | // 28 | // Documentation for most of the stuff can be found here: 29 | // http://www.sgi.com/tech/stl/table_of_contents.html 30 | // 31 | 32 | var assert = require('assert').ok; 33 | 34 | 35 | // 36 | // A queue made out of 2 stacks. 37 | // 38 | // Amortized cost of push: O(1) 39 | // Amortized cost of pop: O(1) 40 | // Amortized cost of top: O(1) 41 | // Cost of remove: O(n) 42 | // 43 | function Queue() { 44 | this._push_stack = []; 45 | this._pop_stack = []; 46 | 47 | this.push.apply(this, arguments); 48 | } 49 | 50 | Queue.prototype = { 51 | push: function(elem) { 52 | for (var i = 0; i < arguments.length; ++i) { 53 | this._push_stack.push(arguments[i]); 54 | } 55 | }, 56 | pop: function() { 57 | if (this.length === 0) { 58 | console.error("INVALID POP"); 59 | throw { message: "Nothing in the Queue to pop" }; 60 | } 61 | var _top = this.top; 62 | this._pop_stack.pop(); 63 | return _top; 64 | }, 65 | remove: function(elem) { 66 | var _tgt_stack = this._pop_stack; 67 | var _tgt_index = -1; 68 | 69 | _tgt_index = this._pop_stack.indexOf(elem); 70 | if (_tgt_index == -1) { 71 | _tgt_stack = this._push_stack; 72 | _tgt_index = this._push_stack.indexOf(elem); 73 | } 74 | 75 | if (_tgt_index != -1) { 76 | _tgt_stack.splice(_tgt_index, 1); 77 | } 78 | }, 79 | _copy_push_to_pop: function() { 80 | this._push_stack.reverse(); 81 | this._pop_stack = this._push_stack; 82 | this._push_stack = [ ]; 83 | } 84 | }; 85 | 86 | Queue.prototype.__defineGetter__('top', function() { 87 | if (this.length === 0) { 88 | return; 89 | } 90 | 91 | if (this._pop_stack.length === 0) { 92 | this._copy_push_to_pop(); 93 | } 94 | 95 | return this._pop_stack.slice(-1)[0]; 96 | }); 97 | 98 | Queue.prototype.__defineGetter__('length', function() { 99 | return this._push_stack.length + this._pop_stack.length; 100 | }); 101 | 102 | 103 | exports.Queue = Queue; 104 | exports.FIFO = Queue; 105 | 106 | 107 | // 108 | // A stack has the following operations: 109 | // push: O(1) 110 | // pop: O(1) 111 | // top: O(1) 112 | // 113 | function Stack() { 114 | } 115 | 116 | Stack.prototype = Array; 117 | Stack.prototype.__defineGetter__('top', function () { 118 | return this.slice(this.length - 1)[0]; 119 | }); 120 | 121 | exports.Stack = Stack; 122 | exports.LIFO = Stack; 123 | exports.FILO = Stack; 124 | 125 | 126 | // 127 | // Comparators: 128 | // Generate GT(>) from LT(<) 129 | // 130 | function cmp_lt(lhs, rhs) { 131 | return lhs < rhs; 132 | } 133 | 134 | function cmp_gt_gen(cmp_lt) { 135 | return function(lhs, rhs) { 136 | return cmp_lt(rhs, lhs); 137 | }; 138 | } 139 | 140 | function cmp_eq_gen(cmp_lt) { 141 | return function(lhs, rhs) { 142 | return !cmp_lt(lhs, rhs) && !cmp_lt(rhs, lhs); 143 | }; 144 | } 145 | 146 | function cmp_lt_eq_gen(cmp_lt) { 147 | var cmp_eq = cmp_eq_gen(cmp_lt); 148 | return function(lhs, rhs) { 149 | return cmp_lt(lhs, rhs) || cmp_eq(rhs, lhs); 150 | }; 151 | } 152 | 153 | function cmp_gt_eq_gen(cmp_lt) { 154 | return function(lhs, rhs) { 155 | return !cmp_lt(lhs, rhs); 156 | }; 157 | } 158 | 159 | 160 | var cmp_gt = cmp_gt_gen(cmp_lt); 161 | var cmp_eq = cmp_eq_gen(cmp_lt); 162 | var cmp_lt_eq = cmp_lt_eq_gen(cmp_lt); 163 | var cmp_gt_eq = cmp_gt_eq_gen(cmp_lt); 164 | 165 | 166 | 167 | function js_cmp_gen(cmp_lt) { 168 | var cmp_gt = cmp_gt_gen(cmp_lt); 169 | return function(lhs, rhs) { 170 | return (cmp_lt(lhs, rhs) ? -1 : (cmp_gt(lhs, rhs) ? 1 : 0)); 171 | }; 172 | } 173 | 174 | exports.cmp_gt_gen = cmp_gt_gen; 175 | exports.cmp_eq_gen = cmp_eq_gen; 176 | exports.cmp_gt_eq_gen = cmp_gt_eq_gen; 177 | exports.cmp_lt_eq_gen = cmp_lt_eq_gen; 178 | 179 | exports.cmp_lt = cmp_lt; 180 | exports.cmp_gt = cmp_gt; 181 | exports.cmp_lt_eq = cmp_lt_eq; 182 | exports.cmp_gt_eq = cmp_gt_eq; 183 | exports.cmp_eq = cmp_eq; 184 | 185 | 186 | exports.js_cmp_gen = js_cmp_gen; 187 | 188 | 189 | 190 | // 191 | // A heap has the following operations: 192 | // push/insert: O(log n) 193 | // pop: O(log n) 194 | // top: O(1) 195 | // constructor: O(n log n) 196 | // 197 | function Heap(cmp, repr) { 198 | this._cmp = cmp || cmp_lt; 199 | this._repr = repr || [ ]; 200 | 201 | if (this._repr.length > 0) { 202 | this._make_heap(); 203 | } 204 | } 205 | 206 | Heap.prototype = { 207 | pop: function() { 208 | var _top = this.top; 209 | 210 | // console.log("REPR:", this._repr); 211 | 212 | // We assume that there is at least 1 element in the heap 213 | var _bot = this._repr.pop(); 214 | 215 | if (this.length > 0) { 216 | this._repr[0] = _bot; 217 | this._bubble_down(0); 218 | } 219 | return _top; 220 | }, 221 | push: function(elem) { 222 | for (var i = 0; i < arguments.length; ++i) { 223 | this._repr.push(arguments[i]); 224 | this._bubble_up(this.length - 1); 225 | } 226 | }, 227 | _make_heap: function() { 228 | // Could be made O(n) later. Currently is O(n log n) 229 | for (var i = 1; i < this._repr.length; ++i) { 230 | this._bubble_up(i); 231 | } 232 | }, 233 | _swap: function(pi, ci) { 234 | return _swap(this._repr, pi, ci); 235 | }, 236 | _bubble_up: function(i) { 237 | // var don = this._repr[i] == 21; 238 | while (i > 0) { 239 | var pi = ((i % 2) === 0 ? i - 2 : i - 1) / 2; 240 | 241 | // If Value at Child is (lt) cmp value at Parent, we swap the 2. 242 | // if (don) { console.log("bubble up: parent", this._repr[pi], "child", this._repr[i]); } 243 | if (this._cmp(this._repr[i], this._repr[pi])) { 244 | // if (don) { console.log("swapped"); } 245 | this._swap(pi, i); 246 | i = pi; 247 | } 248 | else { 249 | i = 0; 250 | } 251 | } 252 | // if (don) { console.log("_repr:", this._repr); } 253 | }, 254 | _bubble_down: function(i) { 255 | var _eof = false; 256 | var self = this; 257 | 258 | while (!_eof) { 259 | _eof = true; 260 | var ci1 = i * 2 + 1; 261 | var ci2 = i * 2 + 2; 262 | 263 | var candidates = [ 264 | { index: ci1, value: this._repr[ci1] }, 265 | { index: ci2, value: this._repr[ci2] } 266 | ].filter(function(v) { 267 | return v.index < self._repr.length; 268 | }); 269 | 270 | candidates.sort(function(lhs, rhs) { 271 | return js_cmp_gen(self._cmp)(lhs.value, rhs.value); 272 | }); 273 | 274 | // console.log("Candidates:", candidates); 275 | 276 | if (candidates.length > 0) { 277 | var candidate = candidates[0]; 278 | 279 | if (this._cmp(candidate.value, this._repr[i])) { 280 | // The smallest child is smaller than the value at 'i'. 281 | // We swap the 2. 282 | // console.log("swapping", this._repr[i], "with", candidate.value); 283 | this._swap(i, candidate.index); 284 | _eof = false; 285 | i = candidate.index; 286 | } 287 | } 288 | 289 | } // while (!_eof) 290 | 291 | } // _bubble_down() 292 | 293 | }; 294 | 295 | Heap.prototype.__defineGetter__('top', function() { 296 | return this._repr[0]; 297 | }); 298 | 299 | Heap.prototype.__defineGetter__('length', function() { 300 | return this._repr.length; 301 | }); 302 | 303 | Heap.prototype.insert = Heap.prototype.push; 304 | 305 | 306 | 307 | exports.Heap = Heap; 308 | exports.PriorityQueue = Heap; 309 | exports.MinHeap = function(repr) { 310 | return new Heap(cmp_lt, repr); 311 | }; 312 | exports.MaxHeap = function(repr) { 313 | return new Heap(cmp_gt, repr); 314 | }; 315 | 316 | // Modifies the array in-place (uses extra memory though) 317 | exports.heap_sort = function(repr, cmp) { 318 | cmp = cmp || cmp_lt; 319 | var h = new Heap(cmp, repr); 320 | var tmp = [ ]; 321 | while (h.length > 0) { 322 | tmp.push(h.pop()); 323 | } 324 | tmp.unshift(0, 0); 325 | repr.splice.apply(repr, tmp); 326 | return repr; 327 | }; 328 | 329 | // 330 | // A min-max-heap has the following operations: 331 | // push/insert: O(log n) 332 | // pop_min: O(log n) 333 | // min: O(1) 334 | // pop_max: O(log n) 335 | // max: O(1) 336 | // constructor: O(n log n) 337 | // 338 | // http://www.cs.otago.ac.nz/staffpriv/mike/Papers/MinMaxHeaps/MinMaxHeaps.pdf 339 | // 340 | // Note: lt MUST be a < comparator 341 | // 342 | function MinMaxHeap(lt, repr) { 343 | this._lt = lt || cmp_lt; 344 | this._gt = cmp_gt_gen(this._lt); 345 | this._repr = repr || [ ]; 346 | 347 | if (this._repr.length > 0) { 348 | this._make_heap(); 349 | } 350 | } 351 | 352 | MinMaxHeap.prototype = { 353 | _make_heap: function() { 354 | for (var i = 0; i < this._repr.length; ++i) { 355 | this._bubble_up(i); 356 | // console.log(this._repr.slice(0, i+1).toString()); 357 | } 358 | }, 359 | 360 | _is_level_min_level: function(level) { 361 | return (level % 2) === 0; 362 | }, 363 | 364 | _is_index_min_level: function(i) { 365 | return this._is_level_min_level(Math.floor(Math.log(i+1) / Math.log(2.0))); 366 | }, 367 | 368 | _parent_index: function(i) { 369 | return ((i % 2) === 0 ? i - 2 : i - 1) / 2; 370 | }, 371 | 372 | _grand_parent_index: function(i) { 373 | return this._parent_index(this._parent_index(i)); 374 | }, 375 | 376 | _bubble_up: function(i) { 377 | if (i === 0) { 378 | return; 379 | } 380 | 381 | var pi = this._parent_index(i); 382 | 383 | if (this._is_index_min_level(i)) { 384 | if (this._gt(this._repr[i], this._repr[pi])) { 385 | _swap(this._repr, i, pi); 386 | this._bubble_up_max(pi); 387 | } 388 | else { 389 | this._bubble_up_min(i); 390 | } 391 | } 392 | else { 393 | if (this._lt(this._repr[i], this._repr[pi])) { 394 | _swap(this._repr, i, pi); 395 | this._bubble_up_min(pi); 396 | } 397 | else { 398 | this._bubble_up_max(i); 399 | } 400 | } 401 | }, 402 | 403 | _bubble_up_min: function(i) { 404 | var gpi = this._grand_parent_index(i); 405 | if (i == 0 || gpi < 0) { 406 | return; 407 | } 408 | 409 | if (this._lt(this._repr[i], this._repr[gpi])) { 410 | _swap(this._repr, i, gpi); 411 | this._bubble_up_min(gpi); 412 | } 413 | }, 414 | 415 | _bubble_up_max: function(i) { 416 | var gpi = this._grand_parent_index(i); 417 | if (i == 0 || gpi < 0) { 418 | return; 419 | } 420 | 421 | if (this._gt(this._repr[i], this._repr[gpi])) { 422 | _swap(this._repr, i, gpi); 423 | this._bubble_up_max(gpi); 424 | } 425 | }, 426 | 427 | _get_candidate_nodes: function() { 428 | var ret = [ ]; 429 | for (var i = 0; i < arguments.length; ++i) { 430 | var index = arguments[i]; 431 | ret.push({ 432 | index: index, 433 | value: this._repr[index] 434 | }); 435 | } 436 | return ret; 437 | }, 438 | 439 | _get_valid_children_and_grand_children: function(i) { 440 | var opts = this._get_candidate_nodes(i*2+1, i*2+2, 441 | (i*2+1)*2 + 1, (i*2+1)*2 + 2, 442 | (i*2+2)*2 + 1, (i*2+2)*2 + 2); 443 | 444 | var self = this; 445 | 446 | opts = opts.filter(function(opt) { 447 | return opt.index < self._repr.length; 448 | }); 449 | 450 | return opts; 451 | }, 452 | 453 | _bubble_down: function(i) { 454 | if (this._is_index_min_level(i)) { 455 | this._bubble_down_min(i); 456 | } 457 | else { 458 | this._bubble_down_max(i); 459 | } 460 | }, 461 | 462 | _bubble_down_min: function(i) { 463 | var opts = this._get_valid_children_and_grand_children(i); 464 | var self = this; 465 | 466 | opts.sort(function(lhs, rhs) { 467 | return js_cmp_gen(self._lt)(lhs.value, rhs.value); 468 | }); 469 | 470 | if (opts.length == 0) { 471 | return; 472 | } 473 | 474 | var opt = opts[0]; 475 | 476 | if (opt.index < i*2+3 /* Is i a parent or grandparent of opt? */) { 477 | // Parent 478 | if (opt.value < this._repr[i]) { 479 | _swap(this._repr, opt.index, i); 480 | } 481 | } 482 | else { 483 | // Grandparent 484 | if (opt.value < this._repr[i]) { 485 | _swap(this._repr, opt.index, i); 486 | var _pi = this._parent_index(opt.index); 487 | if (this._repr[_pi] < this._repr[opt.index]) { 488 | _swap(this._repr, opt.index, _pi); 489 | } 490 | this._bubble_down_min(opt.index); 491 | } 492 | } 493 | }, 494 | 495 | _bubble_down_max: function(i) { 496 | var opts = this._get_valid_children_and_grand_children(i); 497 | var self = this; 498 | 499 | opts.sort(function(lhs, rhs) { 500 | return js_cmp_gen(self._lt)(lhs.value, rhs.value); 501 | }); 502 | 503 | if (opts.length == 0) { 504 | return; 505 | } 506 | 507 | var opt = opts[opts.length - 1]; 508 | 509 | if (opt.index < i*2+3 /* Is i a parent or grandparent of opt? */) { 510 | // Parent 511 | if (opt.value > this._repr[i]) { 512 | _swap(this._repr, opt.index, i); 513 | } 514 | } 515 | else { 516 | // Grandparent 517 | if (opt.value > this._repr[i]) { 518 | _swap(this._repr, opt.index, i); 519 | var _pi = this._parent_index(opt.index); 520 | if (this._repr[_pi] > this._repr[opt.index]) { 521 | _swap(this._repr, opt.index, _pi); 522 | } 523 | this._bubble_down_max(opt.index); 524 | } 525 | } 526 | }, 527 | 528 | _move_from_end: function(index) { 529 | if (index < this.length - 1) { 530 | this._repr[index] = this._repr[this._repr.length - 1]; 531 | } 532 | this._repr.pop(); 533 | if (index < this.length) { 534 | this._bubble_down(index); 535 | } 536 | }, 537 | 538 | _min: function() { 539 | return { index: 0, value: this._repr[0] }; 540 | }, 541 | 542 | _max: function() { 543 | if (this.length == 1) { 544 | return this._min(); 545 | } 546 | 547 | var opts = [ 548 | { index: 1, value: this._repr[1] }, 549 | { index: 2, value: this._repr[2] } 550 | ]; 551 | var self = this; 552 | 553 | opts = opts.filter(function(opt) { 554 | return opt.index < self._repr.length; 555 | }); 556 | 557 | opts.sort(function(lhs, rhs) { 558 | return js_cmp_gen(self._lt)(lhs.value, rhs.value); 559 | }); 560 | 561 | if (opts.length == 0) { 562 | return; 563 | } 564 | 565 | var opt = opts[opts.length - 1]; 566 | 567 | return opt; 568 | }, 569 | 570 | push: function(elem) { 571 | for (var i = 0; i < arguments.length; ++i) { 572 | this._repr.push(arguments[i]); 573 | this._bubble_up(this._repr.length - 1); 574 | } 575 | }, 576 | 577 | pop_min: function() { 578 | var _min = this._min(); 579 | this._move_from_end(_min.index); 580 | return _min.value; 581 | }, 582 | 583 | pop_max: function() { 584 | var _max = this._max(); 585 | this._move_from_end(_max.index); 586 | return _max.value; 587 | } 588 | 589 | }; 590 | 591 | MinMaxHeap.prototype.insert = MinMaxHeap.prototype.push; 592 | 593 | MinMaxHeap.prototype.__defineGetter__('length', function() { 594 | return this._repr.length; 595 | }); 596 | 597 | MinMaxHeap.prototype.__defineGetter__('min', function() { 598 | return this._min().value; 599 | }); 600 | 601 | MinMaxHeap.prototype.__defineGetter__('max', function() { 602 | return this._max().value; 603 | }); 604 | 605 | 606 | exports.MinMaxHeap = MinMaxHeap; 607 | exports.PriorityDequeue = MinMaxHeap; 608 | 609 | 610 | 611 | // 612 | // A Trie has the following operations: 613 | // insert: O(length of string to be inserted) 614 | // remove: O(length of string to be removed) 615 | // remove_many: O(items to be removed * avg. length of each item) 616 | // forEach: O(n) 617 | // 618 | function Trie() { 619 | this.root = { lf: false }; 620 | this._length = 0; 621 | } 622 | 623 | Trie.prototype = { 624 | insert: function() { 625 | for (var i = 0; i < arguments.length; ++i) { 626 | this._insert(arguments[i]); 627 | } 628 | }, 629 | 630 | _insert: function(s) { 631 | var r = this.root; 632 | for (var i = 0; i < s.length; ++i) { 633 | var ch = s[i]; 634 | if (!(ch in r)) { 635 | r[ch] = { lf: false }; 636 | } 637 | r = r[ch]; 638 | } 639 | 640 | if (!r.lf) { 641 | r.lf = true; 642 | this._length += 1; 643 | } 644 | 645 | }, 646 | 647 | remove_many: function() { 648 | var ret = 0; 649 | for (var i = 0; i < arguments.length; ++i) { 650 | ret += (this.remove(arguments[i]) ? 1 : 0); 651 | } 652 | return ret; 653 | }, 654 | 655 | remove: function(s) { 656 | var stat = this._remove(s, this.root); 657 | this._length -= (stat ? 1 : 0); 658 | return stat; 659 | }, 660 | 661 | _remove: function(s, r) { 662 | if (!r) { 663 | // console.log("r is falsy, s ==", s); 664 | return false; 665 | } 666 | 667 | if (s.length == 0) { 668 | var lf = r.lf; 669 | r.lf = false; 670 | return lf; 671 | } 672 | 673 | var _r = r[s[0]]; 674 | var stat = this._remove(s.substring(1), _r); 675 | 676 | if (!stat) { 677 | // console.log("Error removing:", s[0], "from", s, _r); 678 | return false; 679 | } 680 | 681 | if (Object.keys(_r).length == 1 && !_r.lf) { 682 | // We can drop this node 683 | delete r[s[0]]; 684 | } 685 | 686 | return true; 687 | }, 688 | 689 | exists: function(s) { 690 | return this._exists(s, this.root); 691 | }, 692 | 693 | _exists: function(s, r) { 694 | if (!r) { 695 | return false; 696 | } 697 | 698 | if (s.length == 0) { 699 | return r.lf; 700 | } 701 | 702 | var _r = r[s[0]]; 703 | return this._exists(s.substring(1), _r); 704 | }, 705 | 706 | _forEach: function(r, proc, accum, i) { 707 | if (!r) { 708 | return 0; 709 | } 710 | 711 | var _i = 0; 712 | if (r.lf) { 713 | proc(accum.join(''), _i + i); 714 | _i += 1; 715 | } 716 | 717 | var keys = Object.keys(r); 718 | keys.sort(); 719 | for (var index in keys) { 720 | var ch = keys[index]; 721 | if (ch != 'lf') { 722 | accum.push(ch); 723 | _i += this._forEach(r[ch], proc, accum, _i + i); 724 | accum.pop(); 725 | } 726 | } 727 | 728 | return _i; 729 | }, 730 | 731 | forEach: function(proc) { 732 | this._forEach(this.root, proc, [], 0); 733 | } 734 | 735 | }; 736 | 737 | Trie.prototype.__defineGetter__('length', function() { 738 | return this._length; 739 | }); 740 | 741 | exports.Trie = Trie; 742 | 743 | 744 | // 745 | // The Disjoint Set Data Structure is explained here: 746 | // 747 | // https://secure.wikimedia.org/wikipedia/en/wiki/Disjoint-set_data_structure 748 | // and here: 749 | // http://www.topcoder.com/tc?module=Static&d1=tutorials&d2=disjointDataStructure 750 | // 751 | // and this implementation supports the following operations: 752 | // 753 | // create: O(1) - The constructor create a DisjointSet with a single element 754 | // representative: O(n) (worst case) - Returns the representative Set for this Set 755 | // union: O(1) - UNIONs 2 sets into one 756 | // 757 | function DisjointSet(value) { 758 | this._length = 1; 759 | this.value = value; 760 | this.parent = this; 761 | // console.log("Set ctor:", this); 762 | } 763 | 764 | DisjointSet.prototype = { 765 | representative: function() { 766 | if (this.parent === this) { 767 | return this; 768 | } 769 | 770 | var p = this.parent.representative(); 771 | this.parent = p; 772 | return p; 773 | }, 774 | 775 | union: function(other_set) { 776 | var this_rep = this.representative(); 777 | var other_rep = other_set.representative(); 778 | // console.log("this_rep, other_rep:", this_rep, other_rep); 779 | 780 | if (this_rep === other_rep) { 781 | return this_rep; 782 | } 783 | 784 | // console.log("other_rep.length:", other_rep.length); 785 | this_rep._length += other_rep.length; 786 | other_rep.parent = this_rep; 787 | 788 | // console.log("union::returning:", this_rep); 789 | return this_rep; 790 | } 791 | 792 | }; 793 | 794 | DisjointSet.prototype.__defineGetter__('length', function() { 795 | var len = this.representative()._length; 796 | // console.log("length:", len); 797 | return len; 798 | }); 799 | 800 | exports.DisjointSet = DisjointSet; 801 | 802 | 803 | // An AVL Tree Node 804 | function AVLTreeNode(value, parent, height, weight, left, right) { 805 | this.value = value; 806 | this.parent = parent; 807 | this.height = height; 808 | this.weight = weight; 809 | this.left = left; 810 | this.right = right; 811 | } 812 | 813 | // 814 | // An AVL tree is a Height Balanced Binary Search Tree 815 | // 816 | // insert: O(log n) 817 | // remove: O(log g) 818 | // find: O(log g) 819 | // min: O(log g) 820 | // max: O(log g) 821 | // successor: O(log n), amortized O(1) 822 | // predecessor: O(log n), amortized O(1) 823 | // lower_bound: O(log n) 824 | // upper_bound: O(log n) 825 | // find_by_rank: O(log n) 826 | // clear: O(1) 827 | // length: O(1) 828 | // height: O(1) 829 | // forEach: O(n) (performs an in-order traversal) 830 | // toGraphviz: O(n) Returns a string that can be fed to Graphviz to 831 | // draw a Tree 832 | // 833 | // References: 834 | // http://en.wikipedia.org/wiki/AVL_tree 835 | // http://en.wikipedia.org/wiki/Tree_rotation 836 | // http://closure-library.googlecode.com/svn/docs/closure_goog_structs_avltree.js.source.html 837 | // http://gcc.gnu.org/viewcvs/trunk/libstdc%2B%2B-v3/include/bits/stl_tree.h?revision=169899&view=markup 838 | // 839 | function AVLTree(_cmp_lt) { 840 | this.cmp_lt = _cmp_lt || cmp_lt; 841 | this.cmp_eq = cmp_eq_gen(this.cmp_lt); 842 | this.hooks = [ ]; 843 | this._gw_ctr = 1; 844 | 845 | for (var i = 1; i < arguments.length; ++i) { 846 | this.hooks.push(arguments[i]); 847 | } 848 | this.root = null; 849 | } 850 | 851 | AVLTree.prototype = { 852 | insert: function(value) { 853 | if (!this.root) { 854 | this.root = new AVLTreeNode(value, null, 0, 1, null, null); 855 | } 856 | else { 857 | var node = this.root; 858 | var prev = null; 859 | 860 | while (node) { 861 | prev = node; 862 | if (this.cmp_lt(value, node.value)) { 863 | node = node.left; 864 | } 865 | else { 866 | node = node.right; 867 | } 868 | } 869 | 870 | // console.log("Actually inserting:", value); 871 | // console.log("\ninsert::nodes:", nodes); 872 | 873 | var nn = new AVLTreeNode(value, prev, 0, 1, null, null); 874 | if (this.cmp_lt(value, prev.value)) { 875 | // value < nodes.prev.value 876 | prev.left = nn; 877 | } 878 | else { 879 | // value > nodes.prev.value 880 | prev.right = nn; 881 | } 882 | 883 | this._rebalance_to_root(nn); 884 | } 885 | }, 886 | 887 | remove: function(value) { 888 | var node = this._find_node(value); 889 | if (!node) { 890 | return; 891 | } 892 | 893 | this._remove(node); 894 | }, 895 | 896 | find: function(value) { 897 | var node = this._find_node(value); 898 | return node; 899 | }, 900 | 901 | lower_bound: function(value) { 902 | var node = this.root; 903 | var ret = null; 904 | 905 | while (node) { 906 | if (!this.cmp_lt(node.value, value)) { 907 | // this.root.value >= value 908 | ret = node; 909 | node = node.left; 910 | } 911 | else { 912 | node = node.right; 913 | } 914 | } 915 | return ret; 916 | }, 917 | 918 | upper_bound: function(value) { 919 | var node = this.root; 920 | var ret = null; 921 | 922 | while (node) { 923 | if (this.cmp_lt(value, node.value)) { 924 | // value < this.root.value 925 | ret = node; 926 | node = node.left; 927 | } 928 | else { 929 | node = node.right; 930 | } 931 | } 932 | return ret; 933 | }, 934 | 935 | find_by_rank: function(rank) { 936 | return this._find_by_rank(this.root, rank); 937 | }, 938 | 939 | clear: function() { 940 | this.root = null; 941 | }, 942 | 943 | items: function() { 944 | var _i = [ ]; 945 | this.forEach(function(value) { 946 | _i.push(value); 947 | }); 948 | return _i; 949 | }, 950 | 951 | toGraphviz: function() { 952 | // Returns a grpahviz consumable tree for plotting 953 | var graph = [ 'fontname=arial', 'node [fontname=arial,fontsize=10]', 'digraph {' ]; 954 | var nodes = [ ]; 955 | var edges = [ ]; 956 | 957 | this.forEach((function(value, node) { 958 | if (node.parent && !node.parent.id) { 959 | node.parent.id = this._gw_ctr++; 960 | } 961 | if (!node.id) { 962 | node.id = this._gw_ctr++; 963 | } 964 | if (node.parent) { 965 | edges.push('"' + node.parent.value + '-' + node.parent.id + '"->"' + node.value + '-' + node.id + '"'); 966 | } 967 | nodes.push('"' + node.value + '-' + node.id + '"'); 968 | }).bind(this)); 969 | 970 | if (edges.length > 0) { 971 | edges.push(''); 972 | } 973 | 974 | graph.push(nodes.join(', '), '}'); 975 | graph.push(edges.join('; '), ''); 976 | return graph.join('\n'); 977 | }, 978 | 979 | forEach: function(proc) { 980 | this._forEach(this.root, proc); 981 | }, 982 | 983 | _forEach: function(node, proc) { 984 | if (node) { 985 | this._forEach(node.left, proc); 986 | proc(node.value, node); 987 | this._forEach(node.right, proc); 988 | } 989 | }, 990 | 991 | _find_by_rank: function(node, rank) { 992 | if (rank > node.weight) { 993 | return null; 994 | } 995 | 996 | var lw = this._has_left_child(node) ? node.left.weight : 0; 997 | var rw = this._has_right_child(node) ? node.right.weight : 0; 998 | 999 | if (rank <= lw) { 1000 | return this._find_by_rank(node.left, rank); 1001 | } 1002 | else if (rank > lw + 1) { 1003 | return this._find_by_rank(node.right, rank - lw - 1); 1004 | } 1005 | else { 1006 | // Must be the root 1007 | return node.value; 1008 | } 1009 | }, 1010 | 1011 | _remove: function(node) { 1012 | // console.log("_remove::node:", node); 1013 | 1014 | var is_leaf = this._is_leaf(node); 1015 | var has_one_child = this._has_one_child(node); 1016 | 1017 | // console.log("is_leaf, has_one_child:", is_leaf, has_one_child); 1018 | 1019 | if (is_leaf || has_one_child) { 1020 | if (is_leaf) { 1021 | // console.log("Node:", node, "is a leaf"); 1022 | if (this._is_root(node)) { 1023 | this.root = null; 1024 | } 1025 | else { 1026 | if (this._is_left_child(node)) { 1027 | // console.log("Setting left child of:", node.parent, "to null"); 1028 | node.parent.left = null; 1029 | } 1030 | else { 1031 | node.parent.right = null; 1032 | } 1033 | this._rebalance_to_root(node.parent); 1034 | } 1035 | } 1036 | else { 1037 | // Only 1 child 1038 | var tgt_node = null; 1039 | if (this._has_left_child(node)) { 1040 | tgt_node = node.left; 1041 | } 1042 | else { 1043 | tgt_node = node.right; 1044 | } 1045 | 1046 | if (this._is_root(node)) { 1047 | this.root = tgt_node; 1048 | // No need to re-balance since this case can occur only 1049 | // if the tree has just 2 nodes 1050 | } 1051 | else { 1052 | if (this._is_left_child(node)) { 1053 | node.parent.left = tgt_node; 1054 | } 1055 | else { 1056 | node.parent.right = tgt_node; 1057 | } 1058 | } 1059 | if (tgt_node) { 1060 | tgt_node.parent = node.parent; 1061 | } 1062 | this._rebalance_to_root(node.parent); 1063 | } 1064 | } 1065 | else { 1066 | // Has 2 children. Find the successor of this node, 1067 | // delete that node and replace the value of this 1068 | // node with that node's value 1069 | var replacement = this.successor(node); 1070 | // console.log("replacement:", replacement); 1071 | this._remove(replacement); 1072 | node.value = replacement.value; 1073 | } 1074 | }, 1075 | 1076 | successor: function(node) { 1077 | if (node.right) { 1078 | node = node.right; 1079 | while (node && node.left) { 1080 | node = node.left; 1081 | } 1082 | return node; 1083 | } 1084 | else { 1085 | while (node.parent && this._is_right_child(node)) { 1086 | node = node.parent; 1087 | } 1088 | // node is node.parent's left child or null (if node is the root) 1089 | node = node.parent; 1090 | return node; 1091 | } 1092 | }, 1093 | 1094 | predecessor: function(node) { 1095 | if (node.left) { 1096 | node = node.left; 1097 | while (node && node.right) { 1098 | node = node.right; 1099 | } 1100 | return node; 1101 | } 1102 | else { 1103 | while (node.parent && this._is_left_child(node)) { 1104 | node = node.parent; 1105 | } 1106 | // node is node.parent's right child or null (if node is the root) 1107 | node = node.parent; 1108 | return node; 1109 | } 1110 | }, 1111 | 1112 | _is_leaf: function(node) { 1113 | return !node.left && !node.right; 1114 | }, 1115 | 1116 | _has_one_child: function(node) { 1117 | return this._has_left_child(node) + this._has_right_child(node) == 1; 1118 | }, 1119 | 1120 | _has_left_child: function(node) { 1121 | return !!node.left; 1122 | }, 1123 | 1124 | _has_right_child: function(node) { 1125 | return !!node.right; 1126 | }, 1127 | 1128 | _update_metadata: function(node) { 1129 | if (!node) { 1130 | return; 1131 | } 1132 | 1133 | var height = Math.max( 1134 | (node.left ? node.left.height : 0), 1135 | (node.right ? node.right.height : 0) 1136 | ) + 1; 1137 | 1138 | var weight = (node.left ? node.left.weight : 0) + 1139 | (node.right ? node.right.weight : 0) + 1; 1140 | 1141 | // console.log("\nvalue, height, weight:", node.value, height, weight); 1142 | node.height = height; 1143 | node.weight = weight; 1144 | 1145 | // Provide a set of "hook" methods to the user so that the user may 1146 | // add custom fields to the AVLTreeNode. Useful for doing stuff like: 1147 | // sum, min, max in O(1) 1148 | this.hooks.forEach(function(hook) { 1149 | hook(node); 1150 | }); 1151 | 1152 | }, 1153 | 1154 | _update_metadata_upto_root: function(node) { 1155 | while (node) { 1156 | this._update_metadata(node); 1157 | node = node.parent; 1158 | } 1159 | }, 1160 | 1161 | _is_root: function(node) { 1162 | return !node.parent; 1163 | }, 1164 | 1165 | _is_left_child: function(node) { 1166 | if (!node) { 1167 | return false; 1168 | } 1169 | return node.parent.left === node; 1170 | }, 1171 | 1172 | _is_right_child: function(node) { 1173 | if (!node) { 1174 | return false; 1175 | } 1176 | return node.parent.right === node; 1177 | }, 1178 | 1179 | _find_node: function(value) { 1180 | var node = this.lower_bound(value); 1181 | if (node && this.cmp_eq(node.value, value)) { 1182 | return node; 1183 | } 1184 | else { 1185 | return null; 1186 | } 1187 | }, 1188 | 1189 | _rotate_left: function(node) { 1190 | if (!node) { 1191 | return; 1192 | } 1193 | assert(node.right !== null); 1194 | var tmp = node.right; 1195 | 1196 | if (this._is_root(node)) { 1197 | this.root = node.right; 1198 | this.root.parent = null; 1199 | } 1200 | else if (this._is_left_child(node)) { 1201 | node.parent.left = node.right; 1202 | node.right.parent = node.parent; 1203 | } 1204 | else { 1205 | // Must be a right child 1206 | node.parent.right = node.right; 1207 | node.right.parent = node.parent; 1208 | } 1209 | 1210 | node.right = tmp.left; 1211 | if (tmp.left) { 1212 | tmp.left.parent = node; 1213 | } 1214 | tmp.left = node; 1215 | node.parent = tmp; 1216 | 1217 | this._update_metadata(node); 1218 | this._update_metadata(tmp); 1219 | }, 1220 | 1221 | _rotate_right: function(node) { 1222 | if (!node) { 1223 | return; 1224 | } 1225 | assert(node.left !== null); 1226 | var tmp = node.left; 1227 | 1228 | if (this._is_root(node)) { 1229 | this.root = tmp; 1230 | this.root.parent = null; 1231 | } 1232 | else if (this._is_left_child(node)) { 1233 | node.parent.left = tmp; 1234 | tmp.parent = node.parent; 1235 | } 1236 | else { 1237 | // Must be a right child 1238 | node.parent.right = tmp; 1239 | tmp.parent = node.parent; 1240 | } 1241 | 1242 | node.left = tmp.right; 1243 | if (tmp.right) { 1244 | tmp.right.parent = node; 1245 | } 1246 | tmp.right = node; 1247 | node.parent = tmp; 1248 | 1249 | this._update_metadata(node); 1250 | this._update_metadata(tmp); 1251 | }, 1252 | 1253 | _balance_factor: function(node) { 1254 | if (!node) { 1255 | return 0; 1256 | } 1257 | 1258 | var lh = node.left ? node.left.height : 0; 1259 | var rh = node.right ? node.right.height : 0; 1260 | 1261 | // console.log("_balance_factor::of:", node.value, "is:", lh-rh); 1262 | return lh - rh; 1263 | }, 1264 | 1265 | _rebalance_to_root: function(node) { 1266 | while (node) { 1267 | this._rebalance(node); 1268 | node = node.parent; 1269 | } 1270 | }, 1271 | 1272 | _rebalance: function(node) { 1273 | this._update_metadata(node); 1274 | var bf = this._balance_factor(node); 1275 | var _bf; 1276 | 1277 | if (bf > 1) { 1278 | // Do a right rotation since the left subtree is > the right subtree 1279 | _bf = this._balance_factor(node.left); 1280 | if (_bf < 0) { 1281 | this._rotate_left(node.left); 1282 | } 1283 | this._update_metadata(node.left); 1284 | this._rotate_right(node); 1285 | } 1286 | else if (bf < -1) { 1287 | // Do a left rotation since the right subtree is > the left subtree 1288 | _bf = this._balance_factor(node.right); 1289 | if (_bf > 0) { 1290 | this._rotate_right(node.right); 1291 | } 1292 | this._update_metadata(node.right); 1293 | this._rotate_left(node); 1294 | } 1295 | 1296 | // update metadata for 'node' 1297 | this._update_metadata(node); 1298 | } 1299 | }; 1300 | 1301 | AVLTree.prototype.__defineGetter__('height', function() { 1302 | return this.root ? this.root.height : 0; 1303 | }); 1304 | 1305 | AVLTree.prototype.__defineGetter__('length', function() { 1306 | return this.root ? this.root.weight : 0; 1307 | }); 1308 | 1309 | AVLTree.prototype.__defineGetter__('min', function() { 1310 | return this.length ? this.find_by_rank(1) : null; 1311 | }); 1312 | 1313 | AVLTree.prototype.__defineGetter__('max', function() { 1314 | return this.length ? this.find_by_rank(this.length) : null; 1315 | }); 1316 | 1317 | exports.AVLTree = AVLTree; 1318 | 1319 | 1320 | 1321 | 1322 | 1323 | function _swap(range, i, j) { 1324 | var t = range[i]; 1325 | range[i] = range[j]; 1326 | range[j] = t; 1327 | } 1328 | 1329 | // 1330 | // A range [begin, end) 1331 | // 1332 | // A range is a sub-range of another range. 1333 | // It just calls the slice function on the underlying range. 1334 | // Can be used on an array or an arguments object. 1335 | // 1336 | function range(range, begin, end) { 1337 | return Array.prototype.slice.call(range, begin, end); 1338 | } 1339 | 1340 | 1341 | // Time Complexity: O(log n) 1342 | // Space Complexity: O(1) 1343 | function lower_bound(range, value, cmp_lt) { 1344 | /* Returns the first index before which it is safe to insert 'value' 1345 | * such that 'range' remains sorted 1346 | */ 1347 | if (range.length === 0) { 1348 | return 0; 1349 | } 1350 | 1351 | cmp_lt = cmp_lt || exports.cmp_lt; 1352 | var cmp_gt_eq = cmp_gt_eq_gen(cmp_lt); 1353 | 1354 | var b = 0; 1355 | var e = range.length; 1356 | var m = Math.floor(b + (e-b) / 2); 1357 | var lb = e; 1358 | 1359 | while (b < e) { 1360 | if (cmp_gt_eq(range[m], value)) { 1361 | lb = m; 1362 | e = m; 1363 | } 1364 | else { 1365 | b = m + 1; 1366 | } 1367 | m = Math.floor(b + (e-b) / 2); 1368 | } 1369 | return lb; 1370 | } 1371 | 1372 | // Time Complexity: O(log n) 1373 | // Space Complexity: O(1) 1374 | function upper_bound(range, value, cmp_lt) { 1375 | /* Returns the last index before which it is safe to insert 'value' 1376 | * such that 'range' remains sorted 1377 | */ 1378 | if (range.length === 0) { 1379 | return 0; 1380 | } 1381 | 1382 | cmp_lt = cmp_lt || exports.cmp_lt; 1383 | 1384 | var b = 0; 1385 | var e = range.length; 1386 | var m = Math.floor(b + (e-b) / 2); 1387 | var ub = e; 1388 | 1389 | while (b < e) { 1390 | // if (value < range[m]), go left 1391 | if (cmp_lt(value, range[m])) { 1392 | ub = m; 1393 | // console.log("Setting ub to:", ub); 1394 | e = m; 1395 | } 1396 | else { 1397 | b = m + 1; 1398 | } 1399 | m = Math.floor(b + (e-b) / 2); 1400 | } 1401 | // console.log("ub:", ub); 1402 | return ub; 1403 | } 1404 | 1405 | 1406 | // Time Complexity: O(log n) 1407 | // Space Complexity: O(1) 1408 | function equal_range(range, value, cmp_lt) { 1409 | var lb = lower_bound(range, value, cmp_lt); 1410 | var ub = upper_bound(range, value, cmp_lt); 1411 | return [ lb, ub ]; 1412 | } 1413 | 1414 | 1415 | // Time Complexity: O(log n) 1416 | // Space Complexity: O(1) 1417 | function binary_search(range, value, cmp_lt) { 1418 | var lb = lower_bound(range, value, cmp_lt); 1419 | if (lb == range.length) { 1420 | return -1; 1421 | } 1422 | return cmp_eq_gen(cmp_lt)(range[lb], value) ? lb : -1; 1423 | } 1424 | 1425 | // Time Complexity: O(n) 1426 | // Space Complexity: O(1) 1427 | // Note: This function is unstable 1428 | function partition(range, pivot_index, cmp_lt) { 1429 | cmp_lt = cmp_lt || exports.cmp_lt; 1430 | 1431 | assert(pivot_index < range.length); 1432 | // Swap the pivot with the 1st element of the range 1433 | _swap(range, 0, pivot_index); 1434 | var pivot = range[0]; 1435 | 1436 | var l = 1; 1437 | var u = range.length - 1; 1438 | 1439 | while (true) { 1440 | // console.log("while(true), l, u:", l, u); 1441 | 1442 | // range[l] <= pivot 1443 | while (l < u && !cmp_lt(pivot, range[l])) { 1444 | l += 1; 1445 | } 1446 | 1447 | // console.log("range[u], pivot:", range[u], pivot); 1448 | // range[u] > pivot 1449 | while (l < u && cmp_lt(pivot, range[u])) { 1450 | u -= 1; 1451 | } 1452 | 1453 | if (l === u) { 1454 | // console.log("partition::exiting:", l, "and", u); 1455 | // range[u] > pivot 1456 | if (cmp_lt(pivot, range[u])) { 1457 | --u; 1458 | } 1459 | break; 1460 | } 1461 | 1462 | // console.log("partition::swapping indexes:", l, "and", u); 1463 | _swap(range, u, l); 1464 | // l += 1; 1465 | u -= 1; 1466 | } 1467 | 1468 | // console.log("RET:", range.join(", "), "u:", u); 1469 | _swap(range, 0, u); 1470 | return u; 1471 | } 1472 | 1473 | // Time Complexity: O(n) 1474 | // Space Complexity: O(n) 1475 | // 1476 | // The input array 'range' is partitioned in-place 1477 | // 1478 | // Returns the index into 'range' where the 2nd half of the partition 1479 | // begins 1480 | // 1481 | function stable_partition(range, pivot_index, cmp_lt) { 1482 | var dest = [ ]; 1483 | assert(pivot_index < range.length); 1484 | 1485 | var pivot = range[pivot_index]; 1486 | 1487 | var i; 1488 | // Add all elements < pivot to 'dest' 1489 | for (i = 0; i < range.length; ++i) { 1490 | if (cmp_lt(range[i], pivot)) { 1491 | dest.push(range[i]); 1492 | } 1493 | } 1494 | var ret_idx = dest.length; 1495 | // Add all elements >= pivot to 'dest' 1496 | for (i = 0; i < range.length; ++i) { 1497 | if (!cmp_lt(range[i], pivot)) { 1498 | dest.push(range[i]); 1499 | } 1500 | } 1501 | 1502 | assert(dest.length == range.length); 1503 | 1504 | range.splice(0, range.length); 1505 | range.push.apply(range, dest); 1506 | return ret_idx; 1507 | } 1508 | 1509 | // Time Complexity: O(n) 1510 | // Space Complexity: O(n) 1511 | function merge(range1, range2, cmp_lt) { 1512 | cmp_lt = cmp_lt || exports.cmp_lt; 1513 | var ret = [ ]; 1514 | var i1 = 0; 1515 | var i2 = 0; 1516 | 1517 | while (i1 < range1.length && i2 < range2.length) { 1518 | if (cmp_lt(range1[i1], range2[i2])) { 1519 | ret.push(range1[i1]); 1520 | i1 += 1; 1521 | } 1522 | else { 1523 | ret.push(range2[i2]); 1524 | i2 += 1; 1525 | } 1526 | } 1527 | 1528 | while (i1 < range1.length) { 1529 | ret.push(range1[i1]); 1530 | i1 += 1; 1531 | } 1532 | 1533 | while (i2 < range2.length) { 1534 | ret.push(range2[i2]); 1535 | i2 += 1; 1536 | } 1537 | 1538 | return ret; 1539 | } 1540 | 1541 | 1542 | function is_sorted(range, cmp) { 1543 | cmp = cmp || cmp_lt; 1544 | for (var i = 1; i < range.length; ++i) { 1545 | if (cmp(range[i], range[i-1])) { 1546 | return false; 1547 | } 1548 | } 1549 | return true; 1550 | } 1551 | 1552 | function is_heap(range, cmp) { 1553 | cmp = cmp || cmp_lt; 1554 | for (var i = 0; i < range.length; ++i) { 1555 | var ci1 = i * 2 + 1; 1556 | var ci2 = i * 2 + 2; 1557 | 1558 | if ((ci1 < range.length && cmp(range[ci1], range[i])) || 1559 | (ci2 < range.length && cmp(range[ci2], range[i]))) { 1560 | return false; 1561 | } 1562 | } 1563 | return true; 1564 | } 1565 | 1566 | function _randomized_select(range, k, cmp) { 1567 | // console.log("_randomized_select:", k); 1568 | 1569 | assert(range.length != 0); 1570 | if (range.length == 1) { 1571 | return range[0]; 1572 | } 1573 | var ri = Math.floor(Math.random()*range.length); 1574 | var pat = range[ri]; 1575 | // console.log("range1: [", range.join(", "), "]"); 1576 | var pi = partition(range, ri, cmp); 1577 | // console.log("range2: [", range.join(", "), "]"); 1578 | // console.log("ri, pi, pat:", ri, pi, pat); 1579 | // console.log("range[pi]:", range[pi], "pat:", pat); 1580 | 1581 | if (k == pi) { 1582 | return range[pi]; 1583 | } 1584 | else if (k < pi) { 1585 | return _randomized_select(range.slice(0, pi+1), k, cmp); 1586 | } 1587 | else { 1588 | return _randomized_select(range.slice(pi+1), k-pi-1, cmp); 1589 | } 1590 | } 1591 | 1592 | // Time Complexity: O(n) [expected] 1593 | // Space Complexity: O(n) [expected] 1594 | function randomized_select(range, k, cmp) { 1595 | cmp = cmp || cmp_lt; 1596 | if (range.length === 0) { 1597 | return null; 1598 | } 1599 | assert(k > 0 && k <= range.length); 1600 | return _randomized_select(range, k-1, cmp); 1601 | } 1602 | 1603 | 1604 | 1605 | 1606 | exports.range = range; 1607 | exports.lower_bound = lower_bound; 1608 | exports.upper_bound = upper_bound; 1609 | exports.equal_range = equal_range; 1610 | exports.binary_search = binary_search; 1611 | exports.partition = partition; 1612 | exports.stable_partition = stable_partition; 1613 | exports.merge = merge; 1614 | exports.is_sorted = is_sorted; 1615 | exports.is_heap = is_heap; 1616 | exports.randomized_select = randomized_select; 1617 | 1618 | // TODO: String processing algorithms 1619 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { "name": "algorithm-js" 2 | ,"version": "0.0.7" 3 | ,"main": "./algorithm.js" 4 | ,"description": "A collection of Data Structures & Algorithms in javascript" 5 | ,"author": "Dhruv Matani" 6 | ,"dependencies": { } 7 | ,"repositories": [{"type": "git" 8 | ,"path": "https://directidhruv@github.com/directidhruv/algorithm-js.git" 9 | }] 10 | ,"homepage": "https://github.com/directidhruv/algorithm-js" 11 | ,"bugs": "https://github.com/directidhruv/algorithm-js/issues" 12 | ,"maintainers": [{"name": "Dhruv" 13 | ,"email": "dhruvbird@gmail.com" 14 | ,"web": "http://dhruvbird.com/" 15 | }] 16 | ,"contributors": [ ] 17 | ,"licenses": [{"type": "MIT"}] 18 | ,"engine": "node" 19 | } 20 | -------------------------------------------------------------------------------- /tests.js: -------------------------------------------------------------------------------- 1 | /* 2 | * Copyright (c) 2011 Dhruv Matani 3 | * 4 | * Permission is hereby granted, free of charge, to any person obtaining a copy 5 | * of this software and associated documentation files (the "Software"), to deal 6 | * in the Software without restriction, including without limitation the rights 7 | * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 8 | * copies of the Software, and to permit persons to whom the Software is 9 | * furnished to do so, subject to the following conditions: 10 | * 11 | * The above copyright notice and this permission notice shall be included in 12 | * all copies or substantial portions of the Software. 13 | * 14 | * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 15 | * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 16 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 17 | * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 18 | * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 19 | * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN 20 | * THE SOFTWARE. 21 | * 22 | */ 23 | 24 | 25 | var algo = require('./algorithm.js'); 26 | var assert = require('assert').ok; 27 | var util = require('util'); 28 | 29 | 30 | function test_queue() { 31 | var q = new algo.Queue(); 32 | q.push(1, 2, 3, 4, 5, 6); 33 | assert(q.pop() == 1, "pop failed"); 34 | 35 | q.push(10, 20, 30, 40, 50, 60); 36 | assert(q.top == 2, "top failed"); 37 | 38 | assert(q.length == 11, "length failed"); 39 | } 40 | 41 | function test_min_heap() { 42 | var mh = new algo.MinHeap(); 43 | mh.push(10, 100, 20, 9, 86, 40, 33, 12, 21, 99, 101, 100); 44 | // console.log(mh); 45 | 46 | assert(algo.is_heap(mh._repr), "Not a heap"); 47 | 48 | var r = mh._repr; 49 | mh._repr = [ ]; 50 | 51 | algo.heap_sort(r); 52 | assert(algo.is_sorted(r), "Not sorted"); 53 | } 54 | 55 | function test_max_heap() { 56 | var r = [ 10, 100, 20, 9, 86, 40, 33, 12, 21, 99, 101, 100 ]; 57 | 58 | // Sort in non-increasing order 59 | r.sort(function(x,y) { return y-x; }); 60 | 61 | algo.heap_sort(r, algo.cmp_gt); 62 | assert(algo.is_sorted(r, algo.cmp_gt), "Not sorted"); 63 | } 64 | 65 | function test_functions() { 66 | var r = [ 10, 100, 20, 9, 86, 40, 33, 12, 21, 99, 101, 100 ]; 67 | 68 | // Sort in non-decreasing order 69 | r.sort(function(x,y) { return x-y; }); 70 | 71 | // console.log(r); 72 | var lb, ub; 73 | 74 | lb = algo.lower_bound(r, 10); 75 | assert(lb == 1); 76 | 77 | lb = algo.lower_bound(r, 20); 78 | assert(lb == 3); 79 | 80 | lb = algo.lower_bound(r, 50); 81 | assert(lb == 7); 82 | 83 | lb = algo.lower_bound(r, 60); 84 | assert(lb == 7); 85 | 86 | lb = algo.lower_bound(r, 70); 87 | assert(lb == 7); 88 | 89 | lb = algo.lower_bound(r, 100); 90 | assert(lb == 9); 91 | 92 | ub = algo.upper_bound(r, 0); 93 | assert(ub == 0); 94 | 95 | ub = algo.upper_bound(r, 10); 96 | assert(ub == 2); 97 | 98 | ub = algo.upper_bound(r, 20); 99 | assert(ub == 4); 100 | 101 | ub = algo.upper_bound(r, 50); 102 | assert(ub == 7); 103 | 104 | ub = algo.upper_bound(r, 70); 105 | assert(ub == 7); 106 | 107 | ub = algo.upper_bound(r, 100); 108 | assert(ub == 11); 109 | 110 | ub = algo.upper_bound(r, 101); 111 | assert(ub == 12); 112 | 113 | ub = algo.upper_bound(r, 102); 114 | assert(ub == 12); 115 | 116 | 117 | var r1 = [ 1, 2, 3, 3, 3, 3, 3, 4, 5, 10, 10, 1011, 1011, 1011, 1011, 2002 ]; 118 | 119 | lb = algo.lower_bound(r1, 0); 120 | assert(lb == 0); 121 | 122 | lb = algo.lower_bound(r1, 1); 123 | assert(lb == 0); 124 | 125 | lb = algo.lower_bound(r1, 3); 126 | assert(lb == 2); 127 | 128 | lb = algo.lower_bound(r1, 2002); 129 | assert(lb == 15); 130 | 131 | lb = algo.lower_bound(r1, 2003); 132 | assert(lb == 16); 133 | 134 | var r2 = [ 782, 1, 21, 3, 11, 29, 23, 22, 829, 91, 90, 89, 45, 46, 47, 19, 201, 191 ]; 135 | var pidx = algo.partition(r2, 12); 136 | assert(pidx == 8); 137 | 138 | var r2 = [10, 33, 19, 102, 9, 99, 999, 1932, 102, 1992, 8, 88, 888, 88, 8, 0, -1, -2, -2, -1, 0]; 139 | var pidx = algo.partition(r2, 3); 140 | assert(pidx == 16); 141 | 142 | var r2 = [10, 33, 19, 102, 9, 99, 999, 1932, 102, 1992, 8, 88, 888, 88, 8, 0, -1, -2, -2, -1, 0]; 143 | var pidx = algo.partition(r2, 5); 144 | assert(pidx == 14); 145 | 146 | var r2 = [10, 33, 19, 102, 9, 99, 999, 1932, 102, 1992, 8, 88, 888, 88, 8, 0, -1, -2, -2, -1, 0]; 147 | var pidx = algo.partition(r2, 20); 148 | assert(pidx == 5); 149 | 150 | var r2 = [10, 33, 19, 102, 9, 99, 999, 1932, 102, 1992, 8, 88, 888, 88, 8, 0, -1, -2, -2, -1, 0]; 151 | var pidx = algo.partition(r2, 0); 152 | assert(pidx == 9); 153 | 154 | var r2 = [10, 33, 19, 102, 9, 99, 999, 1932, 102, 1992, 8, 88, 888, 88, 8, 0, -1, -2, -2, -1, 0]; 155 | var pidx = algo.partition(r2, 9); 156 | assert(pidx == 20); 157 | } 158 | 159 | 160 | function test_min_max_heap() { 161 | var r2 = [10, 33, 19, 102, 9, 99, 999, 1932, 102, 1992, 8, 88, 888, 88, 8, 0, -1, -2, -2, -1, 0]; 162 | var mmh = new algo.MinMaxHeap(algo.cmp_lt, r2); 163 | 164 | assert(mmh.max == 1992); 165 | assert(mmh.min == -2); 166 | 167 | var sorted_mmh = [ ]; 168 | while (mmh.length > 0) { 169 | sorted_mmh.push(mmh.pop_max()); 170 | } 171 | 172 | assert(algo.is_sorted(sorted_mmh, algo.cmp_gt)); 173 | 174 | 175 | mmh = new algo.MinMaxHeap(algo.cmp_lt, r2); 176 | sorted_mmh = [ ]; 177 | while (mmh.length > 0) { 178 | sorted_mmh.push(mmh.pop_min()); 179 | } 180 | 181 | assert(algo.is_sorted(sorted_mmh, algo.cmp_lt)); 182 | } 183 | 184 | function test_trie() { 185 | var t = new algo.Trie(); 186 | t.insert('alros', 'algos', 'alg', 'GET', 'GEL', 'POST'); 187 | 188 | var tiv = [ ]; 189 | t.forEach(function(e, i) { 190 | tiv.push({ value: e, index: i }); 191 | }); 192 | 193 | assert(tiv[0].value === "GEL"); 194 | assert(tiv[0].index === 0); 195 | 196 | assert(tiv[1].value === "GET"); 197 | assert(tiv[1].index === 1); 198 | 199 | assert(tiv[2].value === "POST"); 200 | assert(tiv[2].index === 2); 201 | 202 | assert(tiv[3].value === "alg"); 203 | assert(tiv[3].index === 3); 204 | 205 | assert(tiv[4].value === "algos"); 206 | assert(tiv[4].index === 4); 207 | 208 | assert(tiv[5].value === "alros"); 209 | assert(tiv[5].index === 5); 210 | 211 | // console.log(util.inspect(t, false, 10)); 212 | assert(t.length == 6); 213 | 214 | assert(t.exists('GE') == false); 215 | assert(t.exists('GET') == true); 216 | assert(t.exists('') == false); 217 | 218 | assert(t.remove('GE') == false); 219 | assert(t.remove('GET') == true); 220 | 221 | // console.log("Trie length:", t.length); 222 | assert(t.length == 5); 223 | 224 | assert(t.exists('GE') == false); 225 | assert(t.exists('GET') == false); 226 | assert(t.exists('GEL') == true); 227 | 228 | // console.log(util.inspect(t, false, 10)); 229 | 230 | assert(t.remove_many('alros', 'algos', 'alg', 'GET', 'GEL', 'POST') == 5); 231 | 232 | // console.log(util.inspect(t, false, 10)); 233 | 234 | assert(t.length == 0); 235 | } 236 | 237 | test_queue(); 238 | test_min_heap(); 239 | test_max_heap(); 240 | test_functions(); 241 | test_min_max_heap(); 242 | test_trie(); 243 | 244 | function test_set(n) { 245 | var set = null; 246 | var hash = { }; 247 | for (var i = 0; i < n; ++i) { 248 | var tmp = new algo.DisjointSet(i); 249 | hash[i] = tmp; 250 | 251 | if (!set) { 252 | set = tmp; 253 | } 254 | else { 255 | set = set.union(tmp); 256 | } 257 | // console.log("make_set::set:", set); 258 | } 259 | return hash; 260 | } 261 | 262 | var hash = test_set(10); 263 | var keys = Object.keys(hash); 264 | keys.forEach(function(key) { 265 | assert(hash[key].parent === hash[0]); 266 | }); 267 | 268 | 269 | function is_a_BST(t) { 270 | var a = [ ]; 271 | t.forEach(function(v) { 272 | a.push(v); 273 | }); 274 | // console.log("a:", a); 275 | return algo.is_sorted(a); 276 | } 277 | 278 | function test_avl_tree() { 279 | var t = new algo.AVLTree(); 280 | t.insert(100); 281 | t.insert(200); 282 | t.insert(50); 283 | t.insert(20); 284 | t.insert(10); 285 | t.insert(1); 286 | t.insert(2); 287 | t.insert(3); 288 | 289 | // console.log("\nTree after insertion:", util.inspect(t.root, false, 10)); 290 | 291 | // console.log(t.toGraphviz()); 292 | assert(is_a_BST(t)); 293 | var s = [ 1, 2, 3, 10, 20, 50, 100, 200 ]; 294 | 295 | // console.log("Graphviz:\n", t.toGraphviz()); 296 | 297 | for (var i = 0; i < t.length; ++i) { 298 | // console.log("rank:", i+1, t.find_by_rank(i+1)); 299 | assert(t.find_by_rank(i+1) == s[i]); 300 | } 301 | 302 | assert(t.lower_bound(40).value == 50); 303 | assert(t.upper_bound(40).value == 50); 304 | 305 | assert(t.lower_bound(60).value == 100); 306 | assert(t.upper_bound(60).value == 100); 307 | 308 | // console.log("GW:", t.toGraphviz()); 309 | // console.log(t.lower_bound(100).value); 310 | assert(t.lower_bound(100).value == 100); 311 | assert(t.upper_bound(100).value == 200); 312 | 313 | assert(t.lower_bound(0).value == 1); 314 | assert(t.upper_bound(0).value == 1); 315 | 316 | assert(!t.lower_bound(1000)); 317 | assert(!t.upper_bound(1000)); 318 | 319 | t.remove(20); 320 | 321 | // console.log("\nTree after removal:", util.inspect(t.root, false, 10)); 322 | 323 | assert(is_a_BST(t)); 324 | var s = [ 1, 2, 3, 10, 50, 100, 200 ]; 325 | 326 | for (var i = 0; i < t.length; ++i) { 327 | // console.log("rank:", i+1, t.find_by_rank(i+1)); 328 | assert(t.find_by_rank(i+1) == s[i]); 329 | } 330 | 331 | t = new algo.AVLTree(); 332 | for (var i = 0; i < 1000; ++i) { 333 | t.insert(i); 334 | } 335 | 336 | // console.log("height, weight:", t.height, t.length); 337 | assert(t.height == 10); 338 | assert(t.length == 1000); 339 | 340 | var prev = 0; 341 | var next = t.find(1); 342 | while (next) { 343 | // console.log(next.value); 344 | assert(prev < next.value); 345 | prev = next.value; 346 | next = t.successor(next); 347 | } 348 | 349 | next = 1000; 350 | prev = t.find(999); 351 | while (prev) { 352 | // console.log(next.value); 353 | assert(prev.value < next); 354 | next = prev.value; 355 | prev = t.predecessor(prev); 356 | } 357 | 358 | for (var i = 0; i < 1000; ++i) { 359 | t.remove(i); 360 | } 361 | 362 | // console.log(t); 363 | // console.log("height, weight:", t.height, t.length); 364 | assert(t.height == 0); 365 | assert(t.length == 0); 366 | } 367 | 368 | function test_avl_tree_hooks() { 369 | function obj_lt(lhs, rhs) { 370 | var v1 = lhs.hasOwnProperty('key') ? lhs.key : lhs; 371 | var v2 = rhs.hasOwnProperty('key') ? rhs.key : rhs; 372 | return v1 < v2; 373 | } 374 | 375 | function binary_tree_summer(node) { 376 | var _s = (node.left ? node.left.sum : 0) + 377 | (node.right ? node.right.sum : 0) + node.value.key; 378 | node.sum = _s; 379 | // console.log("key, sum:", node.value.key, _s); 380 | } 381 | 382 | var t = new algo.AVLTree(obj_lt, binary_tree_summer); 383 | t.insert({ key: 45, value: "frodo" }); 384 | t.insert({ key: 40, value: "harry" }); 385 | t.insert({ key: 55, value: "patricia" }); 386 | t.insert({ key: 105, value: "newton" }); 387 | t.insert({ key: 30, value: "einstein" }); 388 | t.insert({ key: 15, value: "charles" }); 389 | t.insert({ key: 100, value: "knuth" }); 390 | 391 | // console.log(t.root); 392 | // console.log("Sum:", t.root.sum); 393 | assert(t.root.sum == 390); 394 | 395 | // console.log("Tree:", t.root); 396 | 397 | function query_sum(node, value) { 398 | // We do the query like we do for a Segment Tree 399 | if (!node) { 400 | return 0; 401 | } 402 | 403 | // console.log("query_sum:", node.value.key); 404 | 405 | if (node.value.key <= value) { 406 | var sub = node.right ? node.right.sum : 0; 407 | // console.log("sub:", sub); 408 | // console.log("_qs:", query_sum(node.right, value)); 409 | return node.sum - sub + query_sum(node.right, value); 410 | } 411 | else { 412 | // node.value.key > value 413 | return query_sum(node.left, value); 414 | } 415 | } 416 | 417 | // Sum of all numbers <= 40 418 | var tmp = query_sum(t.root, 40); 419 | assert(tmp == 85); 420 | 421 | // Sum of all numbers <= 45 422 | var tmp = query_sum(t.root, 45); 423 | assert(tmp == 130); 424 | 425 | 426 | // Sum of all numbers <= 300 427 | var tmp = query_sum(t.root, 300); 428 | assert(tmp == 390); 429 | 430 | // Sum of all numbers <= 100 431 | var tmp = query_sum(t.root, 100); 432 | assert(tmp == 285); 433 | 434 | // Sum of all numbers <= 5 435 | var tmp = query_sum(t.root, 5); 436 | assert(tmp == 0); 437 | } 438 | 439 | function test_avl_tree_multimap() { 440 | var items = [ 4, 9, 2, 5, 4, 2, 1, 2, 3, 2, 1, 7, 3, 2 ]; 441 | 442 | function binary_tree_summer(node) { 443 | var _s = (node.left ? node.left.sum : 0) + 444 | (node.right ? node.right.sum : 0) + node.value; 445 | node.sum = _s; 446 | } 447 | 448 | 449 | var tree = new algo.AVLTree(algo.cmp_lt, binary_tree_summer); 450 | for (var i = 0; i < items.length; ++i) { 451 | tree.insert(items[i]); 452 | // console.log("Items:", tree.items()); 453 | // console.log("Graphviz:", tree.toGraphviz()); 454 | } 455 | 456 | var lb = tree.lower_bound(1); 457 | // console.log("lb(1+0):", lb.value); 458 | assert(lb.value == 1); 459 | 460 | lb = tree.successor(lb); 461 | assert(lb.value == 1); 462 | // console.log("lb(1+1):", lb.value); 463 | 464 | lb = tree.successor(lb); 465 | assert(lb.value == 2); 466 | // console.log("lb(1+2):", lb.value); 467 | 468 | var ub = tree.upper_bound(1); 469 | assert(ub.value == 2); 470 | // console.log("ub(1+0):", ub.value); 471 | 472 | ub = tree.successor(ub); 473 | assert(ub.value == 2); 474 | // console.log("ub(1+1):", ub.value); 475 | 476 | ub = tree.upper_bound(2); 477 | assert(ub.value == 3); 478 | // console.log("ub(2+0):", ub.value); 479 | 480 | var tmp = tree.predecessor(ub); 481 | assert(tmp.value == 2); 482 | // console.log("ub(2-1):", tmp.value); 483 | 484 | ub = tree.successor(ub); 485 | assert(ub.value == 3); 486 | // console.log("ub(2+1):", ub.value); 487 | 488 | 489 | function query_sum(node, value) { 490 | // We do the query like we do for a Segment Tree 491 | if (!node) { 492 | return 0; 493 | } 494 | 495 | // console.log("query_sum:", node.value.key); 496 | 497 | if (node.value <= value) { 498 | var sub = node.right ? node.right.sum : 0; 499 | // console.log("sub:", sub); 500 | // console.log("_qs:", query_sum(node.right, value)); 501 | return node.sum - sub + query_sum(node.right, value); 502 | } 503 | else { 504 | // node.value > value 505 | return query_sum(node.left, value); 506 | } 507 | } 508 | 509 | // console.log("<= 0:", query_sum(tree.root, 0)); 510 | // console.log("<= 1:", query_sum(tree.root, 1)); 511 | // console.log("<= 2:", query_sum(tree.root, 2)); 512 | // console.log("<= 3:", query_sum(tree.root, 3)); 513 | // console.log("<= 4:", query_sum(tree.root, 4)); 514 | // console.log("<= 7:", query_sum(tree.root, 7)); 515 | // console.log("<= 9:", query_sum(tree.root, 9)); 516 | // console.log("<= 20:", query_sum(tree.root, 20)); 517 | 518 | assert(query_sum(tree.root, 0) === 0); 519 | assert(query_sum(tree.root, 1) === 2); 520 | assert(query_sum(tree.root, 2) === 12); 521 | assert(query_sum(tree.root, 3) === 18); 522 | assert(query_sum(tree.root, 4) === 26); 523 | assert(query_sum(tree.root, 7) === 38); 524 | assert(query_sum(tree.root, 9) === 47); 525 | assert(query_sum(tree.root, 20) === 47); 526 | } 527 | 528 | 529 | test_avl_tree(); 530 | test_avl_tree_hooks(); 531 | test_avl_tree_multimap(); 532 | 533 | 534 | function test_randomized_select() { 535 | for (var i = 0; i < 500; ++i) { 536 | var r = [ 782, 1, 21, 3, 11, 29, 23, 22, 829, 91, 90, 89, 45, 46, 47, 19, 201, 191 ]; 537 | var v = algo.randomized_select(r, Math.floor(r.length/2)); 538 | // console.log("randomized_select::median:", v); 539 | // console.log("sorted:", r.sort(algo.js_cmp_gen(algo.cmp_lt))); 540 | assert(v == 45); 541 | } 542 | } 543 | 544 | test_randomized_select(); 545 | 546 | function test_stable_partition() { 547 | // 2 elements with the same key (30 in this case) should not 548 | // change their relative positions. 549 | var a = [ [ 30, 0 ], [ 20, 1 ], [ 20, 2 ], 550 | [ 1, 3 ], [ 5, 4 ], [ 11, 5 ], 551 | [ 30, 6 ], [ 40, 7 ], [ 10, 8 ] ]; 552 | var ret_idx = algo.stable_partition(a, 6, function(lhs, rhs) { 553 | return lhs[0] < rhs[0]; 554 | }); 555 | for (var i = 0; i < a.length; ) { 556 | var j; 557 | for (j = i + 1; j < a.length; ++j) { 558 | if (a[i][0] != a[j][0]) break; 559 | } 560 | if (!algo.is_sorted(algo.range(a, i, j), function(lhs, rhs) { 561 | return lhs[1] < rhs[1]; 562 | })) { 563 | console.log("In partitioned array:\n", a); 564 | console.log("\nThis is not a stable range:\n", algo.range(a, i, j)); 565 | assert(false); 566 | } 567 | i = j; 568 | } 569 | assert(ret_idx == 6); 570 | } 571 | 572 | test_stable_partition(); 573 | --------------------------------------------------------------------------------