1 /** 2 * @fileOverview An utility of collections 3 */ 4 5 /** 6 * Abstract Map 7 * 8 * @class 9 * @author Sijie Guo <sijie0413@gmail.com> 10 */ 11 var Map = function(){}; 12 13 /** 14 * Entry for map 15 * 16 * @class 17 * @author Sijie Guo <sijie0413@gmail.com> 18 * @constructor 19 * 20 * @param {Object} key key 21 * @param {Object} value value 22 */ 23 Map.Entry = function(key, value) { 24 this.key = key; 25 this.value = value; 26 }; 27 28 exports.Map = Map; 29 30 /// 31 /// Tree Map implementation 32 /// 33 34 /** 35 * Tree Map 36 * 37 * @class 38 * @author Sijie Guo <sijie0413@gmail.com> 39 * @constructor 40 * 41 * @param {Function} comparator comparasion function 42 */ 43 var TreeMap = function(comparator) { 44 45 var that = this; 46 47 /**@field comparator used to maintain order in tree map*/ 48 var _compareFunc = comparator; 49 /**@field root entry*/ 50 var _root = null; 51 /**@filed number of entries in the tree*/ 52 var _size = 0; 53 var _modCnt = 0; 54 55 /**@constant COLOR*/ 56 var COLOR = {}; 57 /**@constant RED COLOR*/ 58 COLOR.RED = 0; 59 /**@constant BLACK COLOR*/ 60 COLOR.BLACK = 1; 61 /// 62 /// Private Class 63 /// 64 var Entry = function(key, value, parent) { 65 this.key = key; 66 this.value = value; 67 this.left = null; 68 this.right = null; 69 this.parent = parent; 70 this.color = COLOR.BLACK; 71 }; 72 73 /// 74 /// Private methods 75 /// 76 77 function size() { 78 return _size; 79 } 80 81 /** 82 * Return the map entry of the given key, or null if the map does not contain an entry for the key. 83 * 84 * @param {Object} key given query key 85 * 86 * @return the map entry of the given key, or null if the map does not contain an entry for the key 87 * 88 * @public 89 */ 90 function getEntry(key) { 91 var p = _root; 92 while (null !== p) { 93 var cmp = _compareFunc(key, p.key); 94 if (cmp < 0) { 95 p = p.left; 96 } else if (cmp > 0) { 97 p = p.right; 98 } else { 99 return p; 100 } 101 } 102 return null; 103 } 104 105 function getCeilingEntry(key) { 106 var p = _root; 107 while (null !== p) { 108 var cmp = _compareFunc(key, p.key); 109 if (cmp < 0) { 110 if (null !== p.left) { 111 p = p.left; 112 } else { 113 return p; 114 } 115 } else if (cmp > 0) { 116 if (null !== p.right) { 117 p = p.right; 118 } else { 119 var parent = p.parent; 120 var ch = p; 121 while (null !== parent && ch === parent.right) { 122 ch = parent; 123 parent = parent.parent; 124 } 125 return parent; 126 } 127 } else { 128 return p; 129 } 130 } 131 return null; 132 } 133 134 function getFloorEntry(key) { 135 var p = _root; 136 while (null !== p) { 137 var cmp = _compareFunc(key, p.key); 138 if (cmp > 0) { 139 if (null !== p.right) { 140 p = p.right; 141 } else { 142 return p; 143 } 144 } else if (cmp < 0) { 145 if (null !== p.left) { 146 p = p.left; 147 } else { 148 var parent = p.parent; 149 var ch = p; 150 while (null !== parent && ch === parent.left) { 151 ch = parent; 152 parent = parent.parent; 153 } 154 return parent; 155 } 156 } else { 157 return p; 158 } 159 } 160 return null; 161 } 162 163 function getHigherEntry(key) { 164 var p = _root; 165 while (null !== p) { 166 var cmp = _compareFunc(key, p.key); 167 if (cmp < 0) { 168 if (null !== p.left) { 169 p = p.left; 170 } else { 171 return p; 172 } 173 } else { 174 if (null !== p.right) { 175 p = p.right; 176 } else { 177 var parent = p.parent; 178 var ch = p; 179 while (null !== parent && ch === parent.right) { 180 ch = parent; 181 parent = parent.parent; 182 } 183 return parent; 184 } 185 } 186 } 187 return null; 188 } 189 190 function getLowerEntry(key) { 191 var p = _root; 192 while (null !== p) { 193 var cmp = _compareFunc(key, p.key); 194 if (cmp > 0) { 195 if (null !== p.right) { 196 p = p.right; 197 } else { 198 return p; 199 } 200 } else { 201 if (null !== p.left) { 202 p = p.left; 203 } else { 204 var parent = p.parent; 205 var ch = p; 206 while (null !== parent && ch === parent.left) { 207 ch = parent; 208 parent = parent.parent; 209 } 210 return parent; 211 } 212 } 213 } 214 return null; 215 } 216 217 function put(key, value) { 218 if (!key) { 219 console.log("TreeMap doesn't support null key now."); 220 return; 221 } 222 223 var t = _root; 224 if (null === t) { 225 _root = new Entry(key, value, null); 226 _size = 1; 227 ++_modCnt; 228 return null; 229 } 230 231 var cmp, parent; 232 do { 233 parent = t; 234 cmp = _compareFunc(key, t.key); 235 if (cmp < 0) { 236 t = t.left; 237 } else if (cmp > 0) { 238 t = t.right; 239 } else { 240 var oldValue = t.value; 241 t.value = value; 242 return oldValue; 243 } 244 } while (null !== t); 245 246 var e = new Entry(key, value, parent); 247 if (cmp < 0) { 248 parent.left = e; 249 } else { 250 parent.right = e; 251 } 252 fixAfterInsertion(e); 253 ++_size; 254 ++_modCnt; 255 return null; 256 } 257 258 259 function deleteEntry(p) { 260 ++_modCnt; 261 --_size; 262 263 // copy successor's element to p and then make p point to successor 264 if (null !== p.left && null !== p.right) { 265 var s = successor(p); 266 p.key = s.key; 267 p.value = s.value; 268 p = s; 269 } 270 271 var replacement = null !== p.left ? p.left : p.right; 272 273 if (null !== replacement) { 274 replacement.parent = p.parent; 275 if (null === p.parent) { 276 _root = replacment; 277 } else if (p === p.parent.left) { 278 p.parent.left = replacement; 279 } else { 280 p.parent.right = replacement; 281 } 282 283 p.left = p.right = p.parent = null; 284 285 if (COLOR.BLACK === p.color) { 286 fixAfterDeletion(replacement); 287 } 288 } else if (null === p.parent) { 289 _root = null; 290 } else { 291 if (COLOR.BLACK === p.color) { 292 fixAfterDeletion(p); 293 } 294 295 if (null !== p.parent) { 296 if (p === p.parent.left) { 297 p.parent.left = null; 298 } else if (p === p.parent.right) { 299 p.parent.right = null; 300 } 301 p.parent = null; 302 } 303 } 304 } 305 306 function clear() { 307 ++_modCnt; 308 _size = 0; 309 _root = null; 310 } 311 312 /// 313 /// Red-Black-Tree Blance Operations 314 /// 315 316 function colorOf(p) { 317 return null === p ? COLOR.BLACK : p.color; 318 } 319 320 function setColor(p, color) { 321 if (null !== p) { 322 p.color = color; 323 } 324 } 325 326 function parentOf(p) { 327 return null === p ? null : p.parent; 328 } 329 330 function leftOf(p) { 331 return null === p ? null : p.left; 332 } 333 334 function rightOf(p) { 335 return null === p ? null : p.right; 336 } 337 338 function rotateLeft(p) { 339 if (null === p) { 340 return; 341 } 342 var r = p.right; 343 p.right = r.left; 344 if (null !== r.left) { 345 r.left.parent = p; 346 } 347 r.parent = p.parent; 348 if (null === p.parent) { 349 _root = r; 350 } else if (p.parent.left === p) { 351 p.parent.left = r; 352 } else { 353 p.parent.right = r; 354 } 355 r.left = p; 356 p.parent = r; 357 } 358 359 function rotateRight(p) { 360 if (null === p) { 361 return; 362 } 363 var l = p.left; 364 p.left = l.right; 365 if (null !== l.right) { 366 l.right.parent = p; 367 } 368 l.parent = p.parent; 369 if (null === p.parent) { 370 _root = l; 371 } else if (p.parent.left === p) { 372 p.parent.left = l; 373 } else { 374 p.parent.right = l; 375 } 376 l.right = p; 377 p.parent = l; 378 } 379 380 function fixAfterInsertion(x) { 381 x.color = COLOR.RED; 382 383 var y; 384 while (null !== x && _root !== x && COLOR.RED == x.parent.color) { 385 if (parentOf(x) == leftOf(parentOf(parentOf(x)))) { 386 y = rightOf(parentOf(parentOf(x))); 387 if (COLOR.RED === colorOf(y)) { 388 setColor(parentOf(x), COLOR.BLACK); 389 setColor(y, COLOR.BLACK); 390 setColor(parentOf(parentOf(x)), COLOR.RED); 391 x = parentOf(parentOf(x)); 392 } else { 393 if (rightOf(parent(x)) === x) { 394 x = parentOf(x); 395 rotateLeft(x); 396 } 397 setColor(parentOf(x), COLOR.BLACK); 398 setColor(parentOf(parentOf(x)), COLOR.RED); 399 rotateRight(parentOf(parentOf(x))); 400 } 401 } else { 402 y = leftOf(parentOf(parentOf(x))); 403 if (COLOR.RED === colorOf(y)) { 404 setColor(parentOf(x), COLOR.BLACK); 405 setColor(y, COLOR.BLACK); 406 setColor(parentOf(parentOf(x)), COLOR.RED); 407 x = parentOf(parentOf(x)); 408 } else { 409 if (leftOf(parentOf(x)) === x) { 410 x = parentOf(x); 411 rotateRight(x); 412 } 413 setColor(parentOf(x), COLOR.BLACK); 414 setColor(parentOf(parentOf(x)), COLOR.RED); 415 rotateLeft(parentOf(parentOf(x))); 416 } 417 } 418 } 419 _root.color = COLOR.BLACK; 420 } 421 422 function fixAfterDeletion(x) { 423 var sib; 424 while (_root !== x && COLOR.BLACK === colorOf(x)) { 425 if (leftOf(parentOf(x)) === x) { 426 sib = rightOf(parentOf(x)); 427 428 if (COLOR.RED === colorOf(sib)) { 429 setColor(sib, COLOR.BLACK); 430 setColor(parentOf(x), COLOR.RED); 431 rotateLeft(parentOf(x)); 432 sib = rightOf(parentOf(x)); 433 } 434 435 if (COLOR.BLACK === colorOf(rightOf(sib)) && 436 COLOR.BLACK === colorOf(leftOf(sib))) { 437 setColor(sib, COLOR.RED); 438 x = parentOf(x); 439 } else { 440 if (COLOR.BLACK === colorOf(rightOf(sib))) { 441 setColor(leftOf(sib), BLACK); 442 setColor(sib, COLOR.RED); 443 rotateRight(sib); 444 sib = rightOf(parentOf(x)); 445 } 446 447 setColor(sib, colorOf(parentOf(x))); 448 setColor(parentOf(x), COLOR.BLACK); 449 setColor(rightOf(sib), COLOR.BLACK); 450 rotateLeft(parentOf(x)); 451 x = _root; 452 } 453 } else { 454 sib = leftOf(parentOf(x)); 455 456 if (COLOR.RED === colorOf(sib)) { 457 setColor(sib, COLOR.BLACK); 458 setColor(parentOf(x), COLOR.RED); 459 rotateRight(parentOf(x)); 460 sib = leftOf(parentOf(x)); 461 } 462 463 if (COLOR.BLACK === colorOf(rightOf(sib)) && 464 COLOR.BLACK === colorOf(leftOf(sib))) { 465 setColor(sib, COLOR.RED); 466 x = parentOf(x); 467 } else { 468 if (COLOR.BLACK === colorOf(leftOf(sib))) { 469 setColor(rightOf(sib), BLACK); 470 setColor(sib, COLOR.RED); 471 rotateLeft(sib); 472 sib = leftOf(parentOf(x)); 473 } 474 475 setColor(sib, colorOf(parentOf(x))); 476 setColor(parentOf(x), COLOR.BLACK); 477 setColor(leftOf(sib), COLOR.BLACK); 478 rotateRight(parentOf(x)); 479 x = _root; 480 } 481 } 482 } 483 484 setColor(x, COLOR.BLACK); 485 } 486 487 /// 488 /// Navigation methods 489 /// 490 491 function getFirstEntry() { 492 var p = _root; 493 if (null !== p) { 494 while (null !== p.left) { 495 p = p.left; 496 } 497 } 498 return p; 499 } 500 501 function getLastEntry() { 502 var p = _root; 503 if (null !== p) { 504 while (null !== p.right) { 505 p = p.right; 506 } 507 } 508 return p; 509 } 510 511 function successor(p) { 512 var t, ch; 513 if (null === p) { 514 return null; 515 } else if (null !== p.right) { 516 t = p.right; 517 while (null !== t.left) { 518 t = t.left; 519 } 520 return t; 521 } else { 522 t = p.parent; 523 ch = p; 524 while (null !== t && ch === t.right) { 525 ch = t; 526 t = t.parent; 527 } 528 return t; 529 } 530 } 531 532 function predecessor(p) { 533 var t, ch; 534 if (null === p) { 535 return null; 536 } else if (null !== p.left) { 537 t = p.right; 538 while (null !== t.right) { 539 t = t.right; 540 } 541 return t; 542 } else { 543 t = p.parent; 544 ch = p; 545 while (null !== t && ch === t.left) { 546 ch = t; 547 t = t.parent; 548 } 549 return t; 550 } 551 } 552 553 function exportEntry(p) { 554 if (null !== p) { 555 return new Map.Entry(p.key, p.value); 556 } else { 557 return null; 558 } 559 } 560 561 /// 562 /// Privileged methods 563 /// 564 565 /** 566 * Return the number of key-value mapping in this map 567 * 568 * @return number of key-value mapping 569 */ 570 this.size = size; 571 572 /** 573 * Associate the specified value with the specified key in this map. 574 * If the map previously contained a mapping entry for the key, the old value is replaced. 575 * 576 * @param {object} key key with which the specified value is to be associated 577 * @param {object} value value to be associated with the specified key 578 * 579 * @return the previous value associated with key 580 */ 581 this.put = put; 582 583 /** 584 * Removes the mapping entry for this key from this map if present. 585 * 586 * @param {Object} key key for which mapping should be removed 587 * 588 * @return the previous value associated with key, or null if there was no mapping entry for this key. 589 */ 590 this.remove = function(key) { 591 var p = getEntry(key); 592 if (null === p) { 593 return p; 594 } 595 596 var oldValue = p.value; 597 deleteEntry(p); 598 return oldValue; 599 }; 600 601 /** 602 * Remove all the mapping entries from this map. 603 * @return void 604 */ 605 this.clear = clear; 606 607 /** 608 * Returns true if this map contains a mapping entry for the given key 609 * 610 * @param {Object} key whose presence in this map is to be tested 611 * 612 * @return true if this map contains a mapping entry for the given key 613 */ 614 this.containsKey = function(key) { 615 return getEntry(key) !== null; 616 }; 617 618 /** 619 * Get the value to which the specified key is mapped, 620 * or null if this map contains no mapping for the key 621 * 622 * @param {Object} key the query key 623 * 624 * @return the value or null 625 */ 626 this.get = function(key) { 627 var p = getEntry(key); 628 return null === p ? null : p.value; 629 }; 630 631 /** 632 * Get the first key in this map 633 * @return the first key 634 */ 635 this.firstKey = function() { 636 var e = getFirstEntry(); 637 return null === e ? null : e.key; 638 }; 639 640 /** 641 * Get the last key in this map 642 * @return the last key 643 */ 644 this.lastKey = function() { 645 var e = getLastEntry(); 646 return null === e ? null : e.key; 647 }; 648 649 /** 650 * Get the first entry in this map 651 * @return the first entry 652 */ 653 this.firstEntry = function() { 654 return exportEntry(getFirstEntry()); 655 }; 656 657 /** 658 * Get the last entry in this map 659 * @return the last entry 660 */ 661 this.lastEntry = function() { 662 return exportEntry(getLastEntry()); 663 }; 664 665 /** 666 * Get the first entry and remove it 667 * @return the first entry 668 */ 669 this.pollFirstEntry = function() { 670 var p = getFirstEntry(); 671 var res = exportEntry(p); 672 if (null !== p) { 673 deleteEntry(p); 674 } 675 return res; 676 }; 677 678 /** 679 * Get the last entry and remove it 680 * @return the last entry 681 */ 682 this.pollLastEntry = function() { 683 var p = getLastEntry(); 684 var res = exportEntry(p); 685 if (null !== p) { 686 deleteEntry(p); 687 } 688 return res; 689 }; 690 691 /** 692 * Get the largest entry whose key is lower than the specified key. 693 * 694 * @param {Object} key the specified key 695 * 696 * @return lower entry 697 */ 698 this.lowerEntry = function(key) { 699 return exportEntry(getLowerEntry(key)); 700 }; 701 702 /** 703 * Get the largest entry's whose key is lower than the specified key. 704 * 705 * @param {Object} key the specfied key 706 * 707 * @return lower key 708 */ 709 this.lowerKey = function(key) { 710 var e = getLowerEntry(key); 711 return null === e ? null : e.key; 712 }; 713 714 }; 715 716 exports.TreeMap = TreeMap; 717