1 /** 2 License: public domain 3 Authors: Simon Bürger 4 */ 5 module jive.priorityqueue; 6 7 import jive.internal; 8 import jive.array; 9 import std.algorithm; 10 import std.range; 11 import std.functional; 12 13 /** 14 * Priority queue which allows fast access to the smallest element. 15 * Implemented as binary heap embedded into a jive.Array. 16 */ 17 struct PriorityQueue(V, alias _pred = "a < b") 18 { 19 // NOTE: attributes (pure/nothrow/...) depend on the predicate and will 20 // be inferred by the compiler. No need to state them explicitly 21 22 private Array!V arr; 23 24 mixin PredicateHelper!(_pred, V); 25 26 static if(dynamicPred) 27 { 28 @disable this(); 29 30 this(_pred p) 31 { 32 this.pred = p; 33 } 34 35 /** constructor that gets content from arbitrary range */ 36 this(Stuff)(_pred p, Stuff data) 37 if(isInputRange!Stuff && is(ElementType!Stuff:V)) 38 { 39 this.pred = p; 40 41 static if(hasLength!Stuff) 42 arr.reserve(data.length); 43 44 foreach(ref x; data) 45 push(x); 46 } 47 } 48 else 49 { 50 /** constructor that gets content from arbitrary range */ 51 this(Stuff)(Stuff data) 52 if(isInputRange!Stuff && is(ElementType!Stuff:V)) 53 { 54 static if(hasLength!Stuff) 55 arr.reserve(data.length); 56 57 foreach(ref x; data) 58 push(x); 59 } 60 } 61 62 /** returns: true if set is empty */ 63 bool empty() const @property 64 { 65 return arr.empty; 66 } 67 68 /** returns: number of elements in the set */ 69 size_t length() const @property 70 { 71 return arr.length; 72 } 73 74 /** Allocate memory for s elements. Does nothing if s < length. */ 75 void reserve(size_t s) 76 { 77 arr.reserve(s); 78 } 79 80 /** removes all elements but keeps allocated memory */ 81 void clear() 82 { 83 arr.clear(); 84 } 85 86 /** clear the heap and return all elements (unordered!) */ 87 Array!V release() 88 { 89 return move(arr); 90 } 91 92 /** returns: first element (i.e. the smallest) */ 93 ref inout(V) front() inout @property 94 { 95 return arr.front; 96 } 97 98 /** 99 * Remove the first element (i.e. the smallest) from the queue. 100 * returns: the removed element 101 */ 102 V pop(string file = __FILE__, int line = __LINE__)() 103 { 104 if(boundsChecks && empty) 105 assert(false, boundsCheckMsg!(file, line)); 106 swap(arr[0], arr.back); 107 V r = arr.popBack; 108 if(!empty) 109 percolateDown(0); 110 return r; 111 } 112 113 /** ditto */ 114 alias popFront = pop; 115 116 /** 117 * Add an element to the queue. 118 */ 119 void push(V value) 120 { 121 arr.pushBack(move(value)); 122 percolateUp(cast(int)length-1); 123 } 124 125 /** Add multiple elements to the queue. */ 126 void push(Stuff)(Stuff data) 127 if(!is(Stuff:V) && isInputRange!Stuff && is(ElementType!Stuff:V)) 128 { 129 static if(hasLength!Stuff) 130 arr.reserve(length + data.length, true); 131 132 foreach(ref x; data) 133 pushBack(x); 134 } 135 136 alias pushBack = push; 137 138 /* internals (private) */ 139 140 private int parent(int i) { return (i-1)/2; } 141 private int left(int i) { return 2*i+1; } 142 private int right(int i) { return 2*i+2; } 143 144 private int smallerChild(int i) 145 { 146 if(right(i) < length && pred(arr[right(i)], arr[left(i)])) 147 return right(i); 148 else 149 return left(i); 150 } 151 152 private void percolateUp(int i) 153 { 154 auto x = move(arr[i]); 155 156 for(int p = parent(i); i != 0 && pred(x, arr[p]); i = p, p = parent(i)) 157 arr[i] = move(arr[p]); 158 159 arr[i] = move(x); 160 } 161 162 private void percolateDown(int i) 163 { 164 auto x = move(arr[i]); 165 166 for(int c = smallerChild(i); c < length && pred(arr[c], x); i = c, c = smallerChild(i)) 167 arr[i] = move(arr[c]); 168 169 arr[i] = move(x); 170 } 171 } 172 173 /// basic usage 174 @nogc nothrow pure @safe unittest 175 { 176 // custom predicate turns a min-heap into a max-heap 177 PriorityQueue!int q; 178 q.push(7); 179 q.push(3); 180 q.push(5); 181 q.push(3); 182 183 assert(q.length == 4); // note that duplicates are kept 184 185 assert(q.pop == 3); 186 assert(q.pop == 3); 187 assert(q.pop == 5); 188 assert(q.pop == 7); 189 190 assert(q.empty); 191 } 192 193 /// custom predicate (without state) 194 /*@nogc*/ nothrow pure @safe unittest 195 { 196 // custom predicate turns a min-heap into a max-heap 197 PriorityQueue!(int, "a > b") q; 198 q.push(7); 199 200 q.push([9,2,8,3,4,1,6,5,8,0]); 201 202 assert(q.pop == 9); 203 assert(q.pop == 8); 204 assert(q.pop == 8); 205 assert(q.pop == 7); 206 q.push(18); 207 assert(q.pop == 18); 208 assert(q.pop == 6); 209 assert(q.pop == 5); 210 q.clear; 211 assert(q.empty); 212 } 213 214 /// custom predicate (with state) 215 unittest 216 { 217 // sometimes, you need a custom predicate/comparator that contains state. 218 // For example this int-comparator puts one selected item first, followed 219 // by all other integers in their usual order. 220 struct Cmp 221 { 222 int priority; 223 224 @disable this(); 225 226 this(int p) 227 { 228 this.priority = p; 229 } 230 231 // this is understood as a comparison 'a < b'. 232 bool opCall(int a, int b) 233 { 234 if(b == priority) 235 return false; 236 if(a == priority) 237 return true; 238 return a < b; 239 } 240 } 241 242 // the constructor now takes an instance of the comparator 243 auto q = PriorityQueue!(int, Cmp)(Cmp(3)); 244 245 q.push([2,3,4,1,5,0]); 246 assert(q.pop == 3); 247 assert(q.pop == 0); 248 assert(q.pop == 1); 249 assert(q.pop == 2); 250 assert(q.pop == 4); 251 assert(q.pop == 5); 252 } 253 254 // check move-semantics 255 unittest 256 { 257 struct S 258 { 259 int x = -1; 260 alias x this; 261 this(this) 262 { 263 // default-constructed objects might be copied, others may not. 264 assert(x == -1); 265 } 266 } 267 268 PriorityQueue!S q; 269 q.push(S(2)); 270 q.push(S(4)); 271 q.push(S(3)); 272 q.push(S(1)); 273 q.push(S(0)); 274 assert(q.pop == S(0)); 275 assert(q.pop == S(1)); 276 assert(q.pop == S(2)); 277 }