blob: 0b892938b75f35e3571053988b5dd499b20139d4 [file] [log] [blame]
library graphlib.data.priority_queue;
/// A min-priority queue data structure. This algorithm is derived from Cormen,
/// et al., "Introduction to Algorithms". The basic idea of a min-priority
/// queue is that you can efficiently (in O(1) time) get the smallest key in
/// the queue. Adding and removing elements takes O(log n) time. A key can
/// have its priority decreased in O(log n) time.
class PriorityQueue<T> {
List<Map<String, dynamic>> _arr = [];
Map<T, int> _keyIndices = {};
/// Returns the number of elements in the queue. Takes `O(1)` time.
int get length => _arr.length;
/// Returns the keys that are in the queue. Takes `O(n)` time.
List<T> get keys => _arr.map((Map x) => x['key']).toList();
/// Returns `true` if [key] is in the queue and `false` if not.
bool has(T key) => _keyIndices.containsKey(key);
/// Returns the priority for [key]. If [key] is not present in the queue
/// then this function returns `null`. Takes `O(1)` time.
num priority(T key) {
if (_keyIndices.containsKey(key)) {
var index = _keyIndices[key];
return _arr[index]['priority'];
}
return null;
}
/// Returns the key for the minimum element in this queue. If the queue is
/// empty this function throws a [PriorityQueueException]. Takes `O(1)` time.
T min() {
if (length == 0) {
throw new PriorityQueueException("Queue underflow");
}
return this._arr[0]['key'];
}
/// Inserts a new key into the priority queue. If the key already exists in
/// the queue this function returns `false`; otherwise it will return `true`.
/// Takes `O(n)` time.
bool add(T key, num priority) {
if (!_keyIndices.containsKey(key)) {
final index = _arr.length;
_keyIndices[key] = index;
_arr.add({'key': key, 'priority': priority});
_decrease(index);
return true;
}
return false;
}
/// Removes and returns the smallest key in the queue. Takes `O(log n)` time.
T removeMin() {
_swap(0, _arr.length - 1);
final min = _arr.removeLast();
_keyIndices.remove(min['key']);
_heapify(0);
return min['key'];
}
/// Decreases the priority for [key] to [priority]. If the new priority is
/// greater than the previous priority, this function will throw a [PriorityQueueException].
void decrease(T key, num priority) {
final index = _keyIndices[key];
if (priority > _arr[index]['priority']) {
throw new PriorityQueueException("New priority is greater than current priority. " +
"Key: $key Old: ${_arr[index]['priority']} New: $priority");
}
_arr[index]['priority'] = priority;
_decrease(index);
}
void _heapify(int i) {
int l = 2 * i,
r = l + 1,
largest = i;
if (l < _arr.length) {
largest = _arr[l]['priority'] < _arr[largest]['priority'] ? l : largest;
if (r < _arr.length) {
largest = _arr[r]['priority'] < _arr[largest]['priority'] ? r : largest;
}
if (largest != i) {
_swap(i, largest);
_heapify(largest);
}
}
}
void _decrease(int index) {
num priority = _arr[index]['priority'];
while (index != 0) {
final parent = index >> 1;
if (_arr[parent]['priority'] < priority) {
break;
}
_swap(index, parent);
index = parent;
}
}
void _swap(int i, int j) {
var origArrI = _arr[i];
var origArrJ = _arr[j];
_arr[i] = origArrJ;
_arr[j] = origArrI;
_keyIndices[origArrJ['key']] = i;
_keyIndices[origArrI['key']] = j;
}
}
class PriorityQueueException implements Exception {
final String message;
PriorityQueueException(this.message);
String toString() => message;
}