370 lines
30 KiB
JavaScript
370 lines
30 KiB
JavaScript
(function () {
|
|
|
|
/* Imports */
|
|
var Meteor = Package.meteor.Meteor;
|
|
var _ = Package.underscore._;
|
|
var IdMap = Package['id-map'].IdMap;
|
|
|
|
/* Package-scope variables */
|
|
var MaxHeap, MinHeap, MinMaxHeap;
|
|
|
|
(function () {
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// packages/binary-heap/max-heap.js //
|
|
// //
|
|
////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// Constructor of Heap // 1
|
|
// - comparator - Function - given two items returns a number // 2
|
|
// - options: // 3
|
|
// - initData - Array - Optional - the initial data in a format: // 4
|
|
// Object: // 5
|
|
// - id - String - unique id of the item // 6
|
|
// - value - Any - the data value // 7
|
|
// each value is retained // 8
|
|
// - IdMap - Constructor - Optional - custom IdMap class to store id->index // 9
|
|
// mappings internally. Standard IdMap is used by default. // 10
|
|
MaxHeap = function (comparator, options) { // 11
|
|
if (! _.isFunction(comparator)) // 12
|
|
throw new Error('Passed comparator is invalid, should be a comparison function'); // 13
|
|
var self = this; // 14
|
|
// 15
|
|
// a C-style comparator that is given two values and returns a number, // 16
|
|
// negative if the first value is less than the second, positive if the second // 17
|
|
// value is greater than the first and zero if they are equal. // 18
|
|
self._comparator = comparator; // 19
|
|
// 20
|
|
options = _.defaults(options || {}, { IdMap: IdMap }); // 21
|
|
// 22
|
|
// _heapIdx maps an id to an index in the Heap array the corresponding value // 23
|
|
// is located on. // 24
|
|
self._heapIdx = new options.IdMap; // 25
|
|
// 26
|
|
// The Heap data-structure implemented as a 0-based contiguous array where // 27
|
|
// every item on index idx is a node in a complete binary tree. Every node can // 28
|
|
// have children on indexes idx*2+1 and idx*2+2, except for the leaves. Every // 29
|
|
// node has a parent on index (idx-1)/2; // 30
|
|
self._heap = []; // 31
|
|
// 32
|
|
// If the initial array is passed, we can build the heap in linear time // 33
|
|
// complexity (O(N)) compared to linearithmic time complexity (O(nlogn)) if // 34
|
|
// we push elements one by one. // 35
|
|
if (_.isArray(options.initData)) // 36
|
|
self._initFromData(options.initData); // 37
|
|
}; // 38
|
|
// 39
|
|
_.extend(MaxHeap.prototype, { // 40
|
|
// Builds a new heap in-place in linear time based on passed data // 41
|
|
_initFromData: function (data) { // 42
|
|
var self = this; // 43
|
|
// 44
|
|
self._heap = _.map(data, function (o) { // 45
|
|
return { id: o.id, value: o.value }; // 46
|
|
}); // 47
|
|
// 48
|
|
_.each(data, function (o, i) { // 49
|
|
self._heapIdx.set(o.id, i); // 50
|
|
}); // 51
|
|
// 52
|
|
if (! data.length) // 53
|
|
return; // 54
|
|
// 55
|
|
// start from the first non-leaf - the parent of the last leaf // 56
|
|
for (var i = parentIdx(data.length - 1); i >= 0; i--) // 57
|
|
self._downHeap(i); // 58
|
|
}, // 59
|
|
// 60
|
|
_downHeap: function (idx) { // 61
|
|
var self = this; // 62
|
|
// 63
|
|
while (leftChildIdx(idx) < self.size()) { // 64
|
|
var left = leftChildIdx(idx); // 65
|
|
var right = rightChildIdx(idx); // 66
|
|
var largest = idx; // 67
|
|
// 68
|
|
if (left < self.size()) { // 69
|
|
largest = self._maxIndex(largest, left); // 70
|
|
} // 71
|
|
if (right < self.size()) { // 72
|
|
largest = self._maxIndex(largest, right); // 73
|
|
} // 74
|
|
// 75
|
|
if (largest === idx) // 76
|
|
break; // 77
|
|
// 78
|
|
self._swap(largest, idx); // 79
|
|
idx = largest; // 80
|
|
} // 81
|
|
}, // 82
|
|
// 83
|
|
_upHeap: function (idx) { // 84
|
|
var self = this; // 85
|
|
// 86
|
|
while (idx > 0) { // 87
|
|
var parent = parentIdx(idx); // 88
|
|
if (self._maxIndex(parent, idx) === idx) { // 89
|
|
self._swap(parent, idx) // 90
|
|
idx = parent; // 91
|
|
} else { // 92
|
|
break; // 93
|
|
} // 94
|
|
} // 95
|
|
}, // 96
|
|
// 97
|
|
_maxIndex: function (idxA, idxB) { // 98
|
|
var self = this; // 99
|
|
var valueA = self._get(idxA); // 100
|
|
var valueB = self._get(idxB); // 101
|
|
return self._comparator(valueA, valueB) >= 0 ? idxA : idxB; // 102
|
|
}, // 103
|
|
// 104
|
|
// Internal: gets raw data object placed on idxth place in heap // 105
|
|
_get: function (idx) { // 106
|
|
var self = this; // 107
|
|
return self._heap[idx].value; // 108
|
|
}, // 109
|
|
// 110
|
|
_swap: function (idxA, idxB) { // 111
|
|
var self = this; // 112
|
|
var recA = self._heap[idxA]; // 113
|
|
var recB = self._heap[idxB]; // 114
|
|
// 115
|
|
self._heapIdx.set(recA.id, idxB); // 116
|
|
self._heapIdx.set(recB.id, idxA); // 117
|
|
// 118
|
|
self._heap[idxA] = recB; // 119
|
|
self._heap[idxB] = recA; // 120
|
|
}, // 121
|
|
// 122
|
|
get: function (id) { // 123
|
|
var self = this; // 124
|
|
if (! self.has(id)) // 125
|
|
return null; // 126
|
|
return self._get(self._heapIdx.get(id)); // 127
|
|
}, // 128
|
|
set: function (id, value) { // 129
|
|
var self = this; // 130
|
|
// 131
|
|
if (self.has(id)) { // 132
|
|
if (self.get(id) === value) // 133
|
|
return; // 134
|
|
// 135
|
|
var idx = self._heapIdx.get(id); // 136
|
|
self._heap[idx].value = value; // 137
|
|
// 138
|
|
// Fix the new value's position // 139
|
|
// Either bubble new value up if it is greater than its parent // 140
|
|
self._upHeap(idx); // 141
|
|
// or bubble it down if it is smaller than one of its children // 142
|
|
self._downHeap(idx); // 143
|
|
} else { // 144
|
|
self._heapIdx.set(id, self._heap.length); // 145
|
|
self._heap.push({ id: id, value: value }); // 146
|
|
self._upHeap(self._heap.length - 1); // 147
|
|
} // 148
|
|
}, // 149
|
|
remove: function (id) { // 150
|
|
var self = this; // 151
|
|
// 152
|
|
if (self.has(id)) { // 153
|
|
var last = self._heap.length - 1; // 154
|
|
var idx = self._heapIdx.get(id); // 155
|
|
// 156
|
|
if (idx !== last) { // 157
|
|
self._swap(idx, last); // 158
|
|
self._heap.pop(); // 159
|
|
self._heapIdx.remove(id); // 160
|
|
// 161
|
|
// Fix the swapped value's position // 162
|
|
self._upHeap(idx); // 163
|
|
self._downHeap(idx); // 164
|
|
} else { // 165
|
|
self._heap.pop(); // 166
|
|
self._heapIdx.remove(id); // 167
|
|
} // 168
|
|
} // 169
|
|
}, // 170
|
|
has: function (id) { // 171
|
|
var self = this; // 172
|
|
return self._heapIdx.has(id); // 173
|
|
}, // 174
|
|
empty: function () { // 175
|
|
var self = this; // 176
|
|
return !self.size(); // 177
|
|
}, // 178
|
|
clear: function () { // 179
|
|
var self = this; // 180
|
|
self._heap = []; // 181
|
|
self._heapIdx.clear(); // 182
|
|
}, // 183
|
|
// iterate over values in no particular order // 184
|
|
forEach: function (iterator) { // 185
|
|
var self = this; // 186
|
|
_.each(self._heap, function (obj) { // 187
|
|
return iterator(obj.value, obj.id); // 188
|
|
}); // 189
|
|
}, // 190
|
|
size: function () { // 191
|
|
var self = this; // 192
|
|
return self._heap.length; // 193
|
|
}, // 194
|
|
setDefault: function (id, def) { // 195
|
|
var self = this; // 196
|
|
if (self.has(id)) // 197
|
|
return self.get(id); // 198
|
|
self.set(id, def); // 199
|
|
return def; // 200
|
|
}, // 201
|
|
clone: function () { // 202
|
|
var self = this; // 203
|
|
var clone = new MaxHeap(self._comparator, self._heap); // 204
|
|
return clone; // 205
|
|
}, // 206
|
|
// 207
|
|
maxElementId: function () { // 208
|
|
var self = this; // 209
|
|
return self.size() ? self._heap[0].id : null; // 210
|
|
}, // 211
|
|
// 212
|
|
_selfCheck: function () { // 213
|
|
var self = this; // 214
|
|
for (var i = 1; i < self._heap.length; i++) // 215
|
|
if (self._maxIndex(parentIdx(i), i) !== parentIdx(i)) // 216
|
|
throw new Error("An item with id " + self._heap[i].id + // 217
|
|
" has a parent younger than it: " + // 218
|
|
self._heap[parentIdx(i)].id); // 219
|
|
} // 220
|
|
}); // 221
|
|
// 222
|
|
function leftChildIdx (i) { return i * 2 + 1; } // 223
|
|
function rightChildIdx (i) { return i * 2 + 2; } // 224
|
|
function parentIdx (i) { return (i - 1) >> 1; } // 225
|
|
// 226
|
|
// 227
|
|
////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
}).call(this);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(function () {
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// packages/binary-heap/min-heap.js //
|
|
// //
|
|
////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
MinHeap = function (comparator, options) { // 1
|
|
var self = this; // 2
|
|
MaxHeap.call(self, function (a, b) { // 3
|
|
return -comparator(a, b); // 4
|
|
}, options); // 5
|
|
}; // 6
|
|
// 7
|
|
Meteor._inherits(MinHeap, MaxHeap); // 8
|
|
// 9
|
|
_.extend(MinHeap.prototype, { // 10
|
|
maxElementId: function () { // 11
|
|
throw new Error("Cannot call maxElementId on MinHeap"); // 12
|
|
}, // 13
|
|
minElementId: function () { // 14
|
|
var self = this; // 15
|
|
return MaxHeap.prototype.maxElementId.call(self); // 16
|
|
} // 17
|
|
}); // 18
|
|
// 19
|
|
// 20
|
|
////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
}).call(this);
|
|
|
|
|
|
|
|
|
|
|
|
|
|
(function () {
|
|
|
|
////////////////////////////////////////////////////////////////////////////////////////
|
|
// //
|
|
// packages/binary-heap/min-max-heap.js //
|
|
// //
|
|
////////////////////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// This implementation of Min/Max-Heap is just a subclass of Max-Heap // 1
|
|
// with a Min-Heap as an encapsulated property. // 2
|
|
// // 3
|
|
// Most of the operations are just proxy methods to call the same method on both // 4
|
|
// heaps. // 5
|
|
// // 6
|
|
// This implementation takes 2*N memory but is fairly simple to write and // 7
|
|
// understand. And the constant factor of a simple Heap is usually smaller // 8
|
|
// compared to other two-way priority queues like Min/Max Heaps // 9
|
|
// (http://www.cs.otago.ac.nz/staffpriv/mike/Papers/MinMaxHeaps/MinMaxHeaps.pdf) // 10
|
|
// and Interval Heaps // 11
|
|
// (http://www.cise.ufl.edu/~sahni/dsaac/enrich/c13/double.htm) // 12
|
|
MinMaxHeap = function (comparator, options) { // 13
|
|
var self = this; // 14
|
|
// 15
|
|
MaxHeap.call(self, comparator, options); // 16
|
|
self._minHeap = new MinHeap(comparator, options); // 17
|
|
}; // 18
|
|
// 19
|
|
Meteor._inherits(MinMaxHeap, MaxHeap); // 20
|
|
// 21
|
|
_.extend(MinMaxHeap.prototype, { // 22
|
|
set: function (id, value) { // 23
|
|
var self = this; // 24
|
|
MaxHeap.prototype.set.apply(self, arguments); // 25
|
|
self._minHeap.set(id, value); // 26
|
|
}, // 27
|
|
remove: function (id) { // 28
|
|
var self = this; // 29
|
|
MaxHeap.prototype.remove.apply(self, arguments); // 30
|
|
self._minHeap.remove(id); // 31
|
|
}, // 32
|
|
clear: function () { // 33
|
|
var self = this; // 34
|
|
MaxHeap.prototype.clear.apply(self, arguments); // 35
|
|
self._minHeap.clear(); // 36
|
|
}, // 37
|
|
setDefault: function (id, def) { // 38
|
|
var self = this; // 39
|
|
MaxHeap.prototype.setDefault.apply(self, arguments); // 40
|
|
return self._minHeap.setDefault(id, def); // 41
|
|
}, // 42
|
|
clone: function () { // 43
|
|
var self = this; // 44
|
|
var clone = new MinMaxHeap(self._comparator, self._heap); // 45
|
|
return clone; // 46
|
|
}, // 47
|
|
minElementId: function () { // 48
|
|
var self = this; // 49
|
|
return self._minHeap.minElementId(); // 50
|
|
} // 51
|
|
}); // 52
|
|
// 53
|
|
// 54
|
|
////////////////////////////////////////////////////////////////////////////////////////
|
|
|
|
}).call(this);
|
|
|
|
|
|
/* Exports */
|
|
if (typeof Package === 'undefined') Package = {};
|
|
Package['binary-heap'] = {
|
|
MaxHeap: MaxHeap,
|
|
MinHeap: MinHeap,
|
|
MinMaxHeap: MinMaxHeap
|
|
};
|
|
|
|
})();
|
|
|
|
//# sourceMappingURL=binary-heap.js.map
|