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 /** number of elements this structure can hold without further allocations */ 75 size_t capacity() const pure nothrow @property @safe 76 { 77 return arr.capacity; 78 } 79 80 /** 81 * Allocated heap memory in bytes. 82 * This is recursive if V has a `.memUsage` property. 83 */ 84 size_t memUsage() const pure nothrow @property @trusted 85 { 86 return arr.memUsage; 87 } 88 89 /** Allocate memory for s elements. Does nothing if s < length. */ 90 void reserve(size_t s) 91 { 92 arr.reserve(s); 93 } 94 95 /** removes all elements but keeps allocated memory */ 96 void clear() 97 { 98 arr.clear(); 99 } 100 101 /** clear the heap and return all elements (unordered!) */ 102 Array!V release() 103 { 104 return move(arr); 105 } 106 107 /** returns: first element (i.e. the smallest) */ 108 ref inout(V) front() inout @property 109 { 110 return arr.front; 111 } 112 113 /** 114 * Remove the first element (i.e. the smallest) from the queue. 115 * returns: the removed element 116 */ 117 V pop(string file = __FILE__, int line = __LINE__)() 118 { 119 if(boundsChecks && empty) 120 assert(false, boundsCheckMsg!(file, line)); 121 swap(arr[0], arr.back); 122 V r = arr.popBack; 123 if(!empty) 124 percolateDown(0); 125 return r; 126 } 127 128 /** ditto */ 129 alias popFront = pop; 130 131 /** 132 * Add an element to the queue. 133 */ 134 void push(V value) 135 { 136 arr.pushBack(move(value)); 137 percolateUp(cast(int)length-1); 138 } 139 140 /** Add multiple elements to the queue. */ 141 void push(Stuff)(Stuff data) 142 if(!is(Stuff:V) && isInputRange!Stuff && is(ElementType!Stuff:V)) 143 { 144 static if(hasLength!Stuff) 145 arr.reserve(length + data.length, true); 146 147 foreach(ref x; data) 148 pushBack(x); 149 } 150 151 alias pushBack = push; 152 153 /* internals (private) */ 154 155 private int parent(int i) { return (i-1)/2; } 156 private int left(int i) { return 2*i+1; } 157 private int right(int i) { return 2*i+2; } 158 159 private int smallerChild(int i) 160 { 161 if(right(i) < length && pred(arr[right(i)], arr[left(i)])) 162 return right(i); 163 else 164 return left(i); 165 } 166 167 private void percolateUp(int i) 168 { 169 auto x = move(arr[i]); 170 171 for(int p = parent(i); i != 0 && pred(x, arr[p]); i = p, p = parent(i)) 172 arr[i] = move(arr[p]); 173 174 arr[i] = move(x); 175 } 176 177 private void percolateDown(int i) 178 { 179 auto x = move(arr[i]); 180 181 for(int c = smallerChild(i); c < length && pred(arr[c], x); i = c, c = smallerChild(i)) 182 arr[i] = move(arr[c]); 183 184 arr[i] = move(x); 185 } 186 } 187 188 /// basic usage 189 @nogc nothrow pure @safe unittest 190 { 191 // custom predicate turns a min-heap into a max-heap 192 PriorityQueue!int q; 193 q.push(7); 194 q.push(3); 195 q.push(5); 196 q.push(3); 197 198 assert(q.length == 4); // note that duplicates are kept 199 200 assert(q.pop == 3); 201 assert(q.pop == 3); 202 assert(q.pop == 5); 203 assert(q.pop == 7); 204 205 assert(q.empty); 206 } 207 208 /// custom predicate (without state) 209 /*@nogc*/ nothrow pure @safe unittest 210 { 211 // custom predicate turns a min-heap into a max-heap 212 PriorityQueue!(int, "a > b") q; 213 q.push(7); 214 215 q.push([9,2,8,3,4,1,6,5,8,0]); 216 217 assert(q.pop == 9); 218 assert(q.pop == 8); 219 assert(q.pop == 8); 220 assert(q.pop == 7); 221 q.push(18); 222 assert(q.pop == 18); 223 assert(q.pop == 6); 224 assert(q.pop == 5); 225 q.clear; 226 assert(q.empty); 227 } 228 229 /// custom predicate (with state) 230 unittest 231 { 232 // sometimes, you need a custom predicate/comparator that contains state. 233 // For example this int-comparator puts one selected item first, followed 234 // by all other integers in their usual order. 235 struct Cmp 236 { 237 int priority; 238 239 @disable this(); 240 241 this(int p) 242 { 243 this.priority = p; 244 } 245 246 // this is understood as a comparison 'a < b'. 247 bool opCall(int a, int b) 248 { 249 if(b == priority) 250 return false; 251 if(a == priority) 252 return true; 253 return a < b; 254 } 255 } 256 257 // the constructor now takes an instance of the comparator 258 auto q = PriorityQueue!(int, Cmp)(Cmp(3)); 259 260 q.push([2,3,4,1,5,0]); 261 assert(q.pop == 3); 262 assert(q.pop == 0); 263 assert(q.pop == 1); 264 assert(q.pop == 2); 265 assert(q.pop == 4); 266 assert(q.pop == 5); 267 } 268 269 // check move-semantics 270 unittest 271 { 272 struct S 273 { 274 int x = -1; 275 alias x this; 276 this(this) 277 { 278 // default-constructed objects might be copied, others may not. 279 assert(x == -1); 280 } 281 } 282 283 PriorityQueue!S q; 284 q.push(S(2)); 285 q.push(S(4)); 286 q.push(S(3)); 287 q.push(S(1)); 288 q.push(S(0)); 289 assert(q.pop == S(0)); 290 assert(q.pop == S(1)); 291 assert(q.pop == S(2)); 292 }