package org.basex.util;
import java.util.*;
/**
* A min-heap.
*
* @author BaseX Team 2005-17, BSD License
* @author Leo Woerteler
* @param <K> key type
* @param <V> value type
*/
public final class MinHeap<K, V> {
/** value array. */
private Object[] vals;
/** Comparator. */
private final Comparator<K> comp;
/** Size of the heap. */
private int size;
/**
* Constructs a heap with the given initial capacity and order.
* @param cmp comparator
*/
public MinHeap(final Comparator<K> cmp) {
this(Array.CAPACITY, cmp);
}
/**
* Constructs a heap with the given initial capacity and order.
* @param cap initial capacity
* @param cmp comparator
*/
public MinHeap(final int cap, final Comparator<K> cmp) {
vals = new Object[2 * cap];
comp = cmp;
}
/**
* Inserts the given key/value pair into the heap.
* @param key key
* @param value value
*/
public void insert(final K key, final V value) {
final int s = size << 1;
if(s == vals.length) vals = Array.copy(vals, new Object[s << 1]);
vals[s] = key;
vals[s + 1] = value;
// let the inserted value bubble up to its position
int curr = size++, par = (curr - 1) / 2;
while(curr > 0 && compare(curr, par) < 0) {
swap(curr, par);
curr = par;
par = (curr - 1) / 2;
}
}
/**
* Removes the minimum from this heap.
* @return the removed entry's value
*/
public V removeMin() {
final V val = minValue();
swap(0, --size);
int pos = 0;
while(pos < size / 2) {
int sm = 2 * pos + 1;
if(sm < size - 1 && compare(sm + 1, sm) < 0) sm++;
if(compare(pos, sm) <= 0) break;
swap(pos, sm);
pos = sm;
}
return val;
}
/**
* returns the value of the smallest key from this heap.
* @return value of the smallest key
*/
@SuppressWarnings("unchecked")
private V minValue() {
return (V) vals[1];
}
/**
* Size of this heap.
* @return number of entries
*/
public int size() {
return size;
}
/**
* Checks if this heap is empty.
* @return {@code true} if heap is empty, {@code false} otherwise
*/
public boolean isEmpty() {
return size == 0;
}
/**
* Compares the keys at position {@code i} and {@code j}.
* @param i position of first key
* @param j position of second key
* @return result of check
*/
@SuppressWarnings("unchecked")
private int compare(final int i, final int j) {
final K a = (K) vals[2 * i], b = (K) vals[2 * j];
return comp == null ? ((Comparable<K>) a).compareTo(b) : comp.compare(a, b);
}
/**
* Swaps the entries at position {@code a} and {@code b}.
* @param a first index
* @param b second index
*/
private void swap(final int a, final int b) {
if(a == b) return;
final int k1 = 2 * a, v1 = k1 + 1, k2 = 2 * b, v2 = k2 + 1;
final Object k = vals[k1], v = vals[v1];
vals[k1] = vals[k2];
vals[v1] = vals[v2];
vals[k2] = k;
vals[v2] = v;
}
/**
* Verifies the inner structure of the heap.
* @throws IllegalStateException if the invariants don't hold
*/
void verify() {
verify(0);
}
/**
* Checks if the heap invariant holds for the node at position {@code i}.
* @param i position of the node
* @throws IllegalStateException if the invariants don't hold
*/
private void verify(final int i) {
if(2 * i + 1 < size) {
final int left = 2 * i + 1, right = 2 * (i + 1);
if(compare(i, left) > 0 || right < size && compare(i, right) > 0)
throw new IllegalStateException("Heap invariant doesn'T hold at node " + i + '.');
verify(left);
verify(right);
}
}
@Override
public String toString() {
final StringBuilder sb = new StringBuilder("Heap[");
for(int i = 0; i < size; i++) {
sb.append('(').append(vals[2 * i]).append(", ").append(
vals[2 * i + 1]).append(')');
if(i < size - 1) sb.append(", ");
}
return sb.append(']').toString();
}
}