BinaryHeap.ts 4.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141
  1. //最小二叉堆 https://eloquentjavascript.net/1st_edition/appendix2.html
  2. export class BinaryHeap<T>{
  3. private _content: T[] = [];
  4. private _scoreFunc: (node: T) => number = null;
  5. constructor(func: (node: T) => number) {
  6. this._content = [];
  7. this._scoreFunc = func;
  8. }
  9. insert(element: T) {
  10. //将新元素添加到数组的末尾
  11. this._content.push(element);
  12. //让它冒泡
  13. this._bubbleUp(this._content.length - 1);
  14. }
  15. remove(element: T) {
  16. //要删除一个值,必须在数组中搜索它
  17. let length = this._content.length;
  18. for (let i = 0; i < length; ++i) {
  19. if (this._content[i] != element) continue;
  20. //找到后,重复pop中的过程,将洞填满。
  21. let end = this._content.pop();
  22. //如果弹出的元素是需要删除的元素,那么就完成了
  23. if (i == length - 1) break;
  24. this._content[i] = end;
  25. //否则我们将删除的元素替换为弹出的元素,并允许它根据情况向上或向下浮动
  26. this._bubbleUp(i);
  27. this._sinkDown(i);
  28. break;
  29. }
  30. }
  31. root() {
  32. //存储第一个元素,以便稍后返回
  33. let result = this._content[0];
  34. //获取数组末尾的元素
  35. let end = this._content.pop();
  36. //如果还剩下任何元素,将end元素放在开始位置,让它下沉
  37. if (this._content.length > 0) {
  38. this._content[0] = end;
  39. this._sinkDown(0);
  40. }
  41. return result;
  42. }
  43. size() {
  44. return this._content.length;
  45. }
  46. clear() {
  47. this._content = [];
  48. }
  49. print() {
  50. console.log(this._content);
  51. }
  52. private _sinkDown(idx: number) {
  53. //查找目标元素及其得分
  54. let length = this._content.length;
  55. let element = this._content[idx];
  56. let elemScore = this._scoreFunc(element);
  57. while (true) {
  58. //计算子元素的索引
  59. let child2N = (idx + 1) * 2;
  60. let child1N = child2N - 1;
  61. //这用于存储元素的新位置(如果有的话)
  62. let swap: number = null;
  63. //如果第一个子元素存在(在数组中)
  64. let child1Score = 0;
  65. if (child1N < length) {
  66. //查找并计算它的分数
  67. let child1 = this._content[child1N];
  68. child1Score = this._scoreFunc(child1);
  69. //如果分数小于元素的分数,则需要进行交换
  70. if (child1Score < elemScore) swap = child1N;
  71. }
  72. //对另一个子级做同样的检查
  73. if (child2N < length) {
  74. let child2 = this._content[child2N];
  75. let child2Score = this._scoreFunc(child2);
  76. if (child2Score < (swap == null ? elemScore : child1Score)) swap = child2N;
  77. }
  78. //不需要进一步交换,我们完成了
  79. if (!swap) break;
  80. //否则,交换并继续
  81. this._content[idx] = this._content[swap];
  82. this._content[swap] = element;
  83. idx = swap;
  84. }
  85. }
  86. private _bubbleUp(idx: number) {
  87. //获取必须移动的元素
  88. let element = this._content[idx], score = this._scoreFunc(element);
  89. //当值为0时,元素不能再向上移动了
  90. while (idx > 0) {
  91. //计算父元素的索引,并获取它
  92. let parentN = Math.floor((idx + 1) / 2) - 1, parent = this._content[parentN];
  93. //如果父级的分数较低,事情就会有条不紊,我们也就完事了
  94. if (score >= this._scoreFunc(parent)) break;
  95. //否则,将父元素与当前元素交换并继续
  96. this._content[parentN] = element;
  97. this._content[idx] = parent;
  98. idx = parentN;
  99. }
  100. }
  101. }
  102. // class PathNode {
  103. // id: number = 0;
  104. // count: number = 0;
  105. // constructor(id: number, count: number) {
  106. // this.id = id;
  107. // this.count = count;
  108. // }
  109. // }
  110. // let tree = new BinaryHeap<PathNode>((node: PathNode) => { return node.count; });
  111. // let node = new PathNode(5,24);
  112. // tree.insert(new PathNode(1,8));
  113. // tree.insert(new PathNode(2,37));
  114. // tree.insert(new PathNode(3,65));
  115. // tree.insert(new PathNode(4,54));
  116. // tree.insert(node);
  117. // tree.insert(new PathNode(6,23));
  118. // tree.insert(new PathNode(7,30));
  119. // tree.insert(new PathNode(8,2));
  120. // tree.print();
  121. // node.count = 1;
  122. // tree.remove(node);
  123. // tree.insert(node);
  124. // tree.print();