priority-deque
Version:
A double-ended priority queue based on min-max heaps.
69 lines • 5.12 kB
JavaScript
"use strict";Object.defineProperty(exports,"__esModule",{value:true});exports.PriorityDeque=void 0;const floyd_rivest_1=require("floyd-rivest");var CMP;(function(CMP){CMP[CMP["LT"]=-1]="LT";CMP[CMP["EQ"]=0]="EQ";CMP[CMP["GT"]=1]="GT";})(CMP||(CMP={}));const defaultComp=(a,b)=>{return(typeof a==='number')?(a-(+b))||0:(""+a).localeCompare(""+b);};function isMinLevel(i){return(Math.log2(i+1)&1)===0;}
function extremalDescendant(h,compare,i,C){const len=h.length;const lc=(i<<1)+1;if(lc>=len){return-1;}
const rc=lc+1;if(rc>=len){return lc;}
let m=Math.sign(compare(h[rc],h[lc]))===C?rc:lc;let gc=(i<<2)+3;const end=Math.min(gc+4,len);for(;gc<end;gc++){if(Math.sign(compare(h[gc],h[m]))===C){m=gc;}}
return m;}
class PriorityDeque{constructor(opts={}){this.heap=[];this.limit=Infinity;this.compare=defaultComp;if(typeof opts.compare==='function'){this.compare=opts.compare;}
if(typeof opts.limit==='number'){this.limit=opts.limit;}
if(opts.items&&typeof opts.items[Symbol.iterator]==='function'){this.set(opts.items);}}
clone(){const pd=new PriorityDeque({compare:this.compare,limit:this.limit,});pd.heap=[...this.heap];return pd;}
reheap(i){for(;i>=0;i--){this.trickleDown(i);}}
set(elements){const items=[...elements];const{limit}=this;if(items.length>limit){floyd_rivest_1.select(items,limit,(a,b)=>Math.sign(this.compare(a,b)));items.length=limit;}
this.heap=items;this.reheap(items.length>>1);}
clear(){this.heap.length=0;}
trickleDown(i){const{heap:h,compare}=this;let C=isMinLevel(i)?CMP.LT:CMP.GT;for(;;){const m=extremalDescendant(h,compare,i,C);if(m===-1){return;}
const hi=h[i];const hm=h[m];if(Math.sign(compare(hm,hi))===C){h[i]=hm;h[m]=hi;if(m>((i+1)<<1)){const parent=(m-1)>>1;const hp=h[parent];if(Math.sign(compare(hp,hi))===C){h[m]=hp;h[parent]=hi;}}
else{C=-C;}
i=m;}
else{break;}}}
bubbleUp(i){const{heap,compare}=this;const p=(i-1)>>1;const hi=heap[i];const hp=heap[p];const cmp=Math.sign(compare(hi,hp));let moved=false;let LT=CMP.LT;if(isMinLevel(i)){if(cmp===CMP.GT){heap[i]=hp;heap[p]=hi;i=p;LT=CMP.GT;moved=true;}}
else if(cmp===CMP.LT){heap[i]=hp;heap[p]=hi;i=p;moved=true;}
else{LT=CMP.GT;}
while(i>=3){const gp=(((i-1)>>1)-1)>>1;const hi=heap[i];const hp=heap[gp];if(Math.sign(compare(hi,hp))===LT){heap[i]=hp;heap[gp]=hi;i=gp;moved=true;}
else
break;}
return moved;}
[Symbol.iterator](){return this.heap.values();}
get length(){return this.heap.length;}
push(...elements){this.append(elements);}
append(elements){if(this.limit===0)
return;if(elements.length===0)
return;const{heap,compare,limit}=this;let size=heap.length;const l=elements.length;const addable=Math.min(limit-size,l);let i=0;if(size===0){heap[0]=elements[0];size=1;i=1;}
for(;i<addable;i++,size++){this.heap[size]=elements[i];this.bubbleUp(size);}
if(limit===1){for(;i<l;i++){const e=elements[i];if(Math.sign(compare(e,heap[0]))===CMP.LT){heap[0]=e;}}}
else{let maxI=this.maxIndex();let maxE=heap[maxI];for(;i<l;i++){const e=elements[i];if(Math.sign(compare(e,maxE))!==CMP.LT){continue;}
heap[maxI]=e;if(!this.bubbleUp(maxI)){this.trickleDown(maxI);}
maxI=this.maxIndex();maxE=heap[maxI];}}}
pop(){if(this.heap.length===0){return undefined;}
return this.removeAt(0);}
shift(){if(this.heap.length===0){return undefined;}
return this.removeAt(this.maxIndex());}
unshift(...elements){this.append(elements);}
map(fn,compare=defaultComp){const pd=new PriorityDeque({compare,limit:this.limit});pd.heap=this.heap.map(fn);pd.reheap(this.heap.length>>1);return pd;}
filter(fn){const pd=new PriorityDeque({compare:this.compare,limit:this.limit,});pd.heap=this.heap.filter(fn);pd.reheap(pd.heap.length>>1);return pd;}
collect(fn,compare=defaultComp){const pd=new PriorityDeque({compare,limit:this.limit});const heap=[];let l=0;for(const e of this.heap){for(const v of fn(e)){heap[l++]=v;}}
pd.heap=heap;pd.reheap(l>>1);return pd;}
contains(e){return this.heap.indexOf(e)>-1;}
some(fn){return this.heap.some(fn);}
every(fn){return this.heap.every(fn);}
find(fn){return this.heap.find(fn);}
forEach(fn){this.heap.forEach(fn);}
findMin(){return this.heap.length>0?this.heap[0]:undefined;}
findMax(){return this.heap.length>0?this.heap[this.maxIndex()]:undefined;}
maxIndex(){const{heap}=this;if(heap.length<2){return 0;}
if(heap.length===2){return 1;}
return Math.sign(this.compare(heap[1],heap[2]))===CMP.LT?2:1;}
replaceMin(e){if(this.limit===0){return undefined;}
const{heap}=this;let size=heap.length;const minE=heap[0];heap[0]=e;if(size>0){this.trickleDown(0);}
return minE;}
replaceMax(e){if(this.limit===0){return undefined;}
const{heap}=this;const maxI=this.maxIndex();const maxE=heap[maxI];heap[maxI]=e;if(maxI>0&&!this.bubbleUp(maxI)){this.trickleDown(maxI);}
return maxE;}
removeAt(i){const{heap}=this;const ret=heap[i];const size=heap.length-1;if(size>0){heap[i]=heap[size];this.trickleDown(i);}
heap.length=size;return ret;}
remove(e){const{heap}=this;const i=heap.indexOf(e);if(i===-1){return false;}
this.removeAt(i);return true;}
replace(a,b){const{heap}=this;const i=heap.indexOf(a);if(i===-1){return false;}
heap[i]=b;if(i===0||!this.bubbleUp(i)){this.trickleDown(i);}
return true;}}
exports.PriorityDeque=PriorityDeque;