|
| 1 | +/** |
| 2 | + * KeyPriorityQueue is a priority queue based on a Minimum Binary Heap. |
| 3 | + * |
| 4 | + * Minimum Binary Heaps are binary trees which are filled level by level |
| 5 | + * and then from left to right inside a depth level. |
| 6 | + * Their main property is that any parent node has a smaller or equal priority to all of its children, |
| 7 | + * hence the root of the tree always has the smallest priority of all nodes. |
| 8 | + * |
| 9 | + * This implementation of the Minimum Binary Heap allows for nodes to be associated to both a key, |
| 10 | + * which can be any datatype, and a priority. |
| 11 | + * |
| 12 | + * The heap is represented by an array with nodes ordered |
| 13 | + * from root-to-leaf, left-to-right. |
| 14 | + * Therefore, the parent-child node relationship is such that |
| 15 | + * * the children nodes positions relative to their parent are: (parentPos * 2 + 1) and (parentPos * 2 + 2) |
| 16 | + * * the parent node position relative to either of its children is: Math.floor((childPos - 1) / 2) |
| 17 | + * |
| 18 | + * More information and visuals on Binary Heaps can be found here: https://www.geeksforgeeks.org/binary-heap/ |
| 19 | + */ |
| 20 | + |
| 21 | +// Priority Queue Helper functions |
| 22 | +constgetParentPosition=position=>Math.floor((position-1)/2) |
| 23 | +constgetChildrenPositions=position=>[2*position+1,2*position+2] |
| 24 | + |
| 25 | +classKeyPriorityQueue{ |
| 26 | +// Priority Queue class using Minimum Binary Heap |
| 27 | +constructor(){ |
| 28 | +this._heap=[] |
| 29 | +this.priorities=newMap() |
| 30 | +} |
| 31 | + |
| 32 | +/** |
| 33 | + * Checks if the heap is empty |
| 34 | + *@returns boolean |
| 35 | + */ |
| 36 | +isEmpty(){ |
| 37 | +returnthis._heap.length===0 |
| 38 | +} |
| 39 | + |
| 40 | +/** |
| 41 | + * Adds an element to the queue |
| 42 | + *@param {*} key |
| 43 | + *@param {number} priority |
| 44 | + */ |
| 45 | +push(key,priority){ |
| 46 | +this._heap.push(key) |
| 47 | +this.priorities.set(key,priority) |
| 48 | +this._shiftUp(this._heap.length-1) |
| 49 | +} |
| 50 | + |
| 51 | +/** |
| 52 | + * Removes the element with least priority |
| 53 | + *@returns the key of the element with least priority |
| 54 | + */ |
| 55 | +pop(){ |
| 56 | +this._swap(0,this._heap.length-1) |
| 57 | +constkey=this._heap.pop() |
| 58 | +this.priorities.delete(key) |
| 59 | +this._shiftDown(0) |
| 60 | +returnkey |
| 61 | +} |
| 62 | + |
| 63 | +/** |
| 64 | + * Checks whether a given key is present in the queue |
| 65 | + *@param {*} key |
| 66 | + *@returns boolean |
| 67 | + */ |
| 68 | +contains(key){ |
| 69 | +returnthis.priorities.has(key) |
| 70 | +} |
| 71 | + |
| 72 | +/** |
| 73 | + * Updates the priority of the given element. |
| 74 | + * Adds the element if it is not in the queue. |
| 75 | + *@param {*} key the element to change |
| 76 | + *@param {number} priority new priority of the element |
| 77 | + */ |
| 78 | +update(key,priority){ |
| 79 | +constcurrPos=this._heap.indexOf(key) |
| 80 | +// if the key does not exist yet, add it |
| 81 | +if(currPos===-1)returnthis.push(key,priority) |
| 82 | +// else update priority |
| 83 | +this.priorities.set(key,priority) |
| 84 | +constparentPos=getParentPosition(currPos) |
| 85 | +constcurrPriority=this._getPriorityOrInfinite(currPos) |
| 86 | +constparentPriority=this._getPriorityOrInfinite(parentPos) |
| 87 | +const[child1Pos,child2Pos]=getChildrenPositions(currPos) |
| 88 | +constchild1Priority=this._getPriorityOrInfinite(child1Pos) |
| 89 | +constchild2Priority=this._getPriorityOrInfinite(child2Pos) |
| 90 | + |
| 91 | +if(parentPos>=0&&parentPriority>currPriority){ |
| 92 | +this._shiftUp(currPos) |
| 93 | +}elseif(child1Priority<currPriority||child2Priority<currPriority){ |
| 94 | +this._shiftDown(currPos) |
| 95 | +} |
| 96 | +} |
| 97 | + |
| 98 | +_getPriorityOrInfinite(position){ |
| 99 | +// Helper function, returns priority of the node, or Infinite if no node corresponds to this position |
| 100 | +if(position>=0&&position<this._heap.length)returnthis.priorities.get(this._heap[position]) |
| 101 | +elsereturnInfinity |
| 102 | +} |
| 103 | + |
| 104 | +_shiftUp(position){ |
| 105 | +// Helper function to shift up a node to proper position (equivalent to bubbleUp) |
| 106 | +letcurrPos=position |
| 107 | +letparentPos=getParentPosition(currPos) |
| 108 | +letcurrPriority=this._getPriorityOrInfinite(currPos) |
| 109 | +letparentPriority=this._getPriorityOrInfinite(parentPos) |
| 110 | + |
| 111 | +while(parentPos>=0&&parentPriority>currPriority){ |
| 112 | +this._swap(currPos,parentPos) |
| 113 | +currPos=parentPos |
| 114 | +parentPos=getParentPosition(currPos) |
| 115 | +currPriority=this._getPriorityOrInfinite(currPos) |
| 116 | +parentPriority=this._getPriorityOrInfinite(parentPos) |
| 117 | +} |
| 118 | +} |
| 119 | + |
| 120 | +_shiftDown(position){ |
| 121 | +// Helper function to shift down a node to proper position (equivalent to bubbleDown) |
| 122 | +letcurrPos=position |
| 123 | +let[child1Pos,child2Pos]=getChildrenPositions(currPos) |
| 124 | +letchild1Priority=this._getPriorityOrInfinite(child1Pos) |
| 125 | +letchild2Priority=this._getPriorityOrInfinite(child2Pos) |
| 126 | +letcurrPriority=this._getPriorityOrInfinite(currPos) |
| 127 | + |
| 128 | +if(currPriority===Infinity){ |
| 129 | +return |
| 130 | +} |
| 131 | + |
| 132 | +while(child1Priority<currPriority||child2Priority<currPriority){ |
| 133 | +if(child1Priority<currPriority&&child1Priority<child2Priority){ |
| 134 | +this._swap(child1Pos,currPos) |
| 135 | +currPos=child1Pos |
| 136 | +}else{ |
| 137 | +this._swap(child2Pos,currPos) |
| 138 | +currPos=child2Pos |
| 139 | +} |
| 140 | +[child1Pos,child2Pos]=getChildrenPositions(currPos) |
| 141 | +child1Priority=this._getPriorityOrInfinite(child1Pos) |
| 142 | +child2Priority=this._getPriorityOrInfinite(child2Pos) |
| 143 | +currPriority=this._getPriorityOrInfinite(currPos) |
| 144 | +} |
| 145 | +} |
| 146 | + |
| 147 | +_swap(position1,position2){ |
| 148 | +// Helper function to swap 2 nodes |
| 149 | +[this._heap[position1],this._heap[position2]]=[this._heap[position2],this._heap[position1]] |
| 150 | +} |
| 151 | +} |
| 152 | + |
| 153 | +export{KeyPriorityQueue} |