/*******************************************************************************
* Copyright (c) 2015
*
* Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
*
* The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
* DEALINGS IN THE SOFTWARE.
*******************************************************************************/
package jsettlers.algorithms.heap;
import java.util.ArrayList;
/**
* This is a unsynchronized min heap implementation.
* <p>
* It can heap everything that implements {@link MinHeapable}, but every object that is added to this heap may not be added to an other heap.
*
* @author andreas
* @param <T>
*/
public class MinHeap<T extends MinHeapable> {
private final int MIN_CAPACITY;
private final ArrayList<T> heap = new ArrayList<T>();
private int size = 0;
/**
* Creates a new min heap.
*
* @param capacity
* The minimal and initial capacity the heap should have. May not be null.
*/
public MinHeap(int capacity) {
if (capacity <= 0) {
throw new IllegalArgumentException("too smal initial capacity");
}
MIN_CAPACITY = capacity;
heap.ensureCapacity(MIN_CAPACITY);
}
/**
* Adds a new element to the heap.
*
* @param e
* The element to add.
*/
public void insert(T e) {
if (heap.size() > size) {
heap.set(size, e);
} else {
heap.add(e);
}
siftUp(e, size);
size++;
}
/**
* sifts up an element to reestablish the heap array consistency.
*
* @param e
* The element to sift.
*/
public void siftUp(T e) {
siftUp(e, e.getHeapIdx());
}
/**
* Sifts up an element recursively until the heap consistency is reestablished.
*
* @param e
* The element.
* @param eID
* The index of the element in the heap.
* @return returns if the element was sifted up at least one time
*/
private boolean siftUp(T e, int eID) {
e.setHeapIdx(eID);
int parentID = getParent(eID);
T parentElement = heap.get(parentID);
if (parentElement.getRank() > e.getRank()) {
heap.set(parentID, e);
heap.set(eID, parentElement);
parentElement.setHeapIdx(eID);
siftUp(e, parentID);
return true;
}
return false;
}
/**
* Checks whether the heap is empty.
*
* @return true if and only if the heap is empty.
*/
public final boolean isEmpty() {
return size == 0;
}
/**
* Deletes the element of the heap that has the minimal value.
* <p>
* If the heap is empty, no action is preformed.
*
* @return The deleted element, or null.
*/
public T deleteMin() {
if (size <= 0) {
return null;
}
size--;
T result = heap.get(0);
T last = heap.get(size);
heap.set(0, last);
siftDown(last, 0);
return result;
}
private void siftDown(T e, int pos) {
e.setHeapIdx(pos);
int leftChildID = getLeftChildID(pos);
if (leftChildID < size) {
T leftChild = heap.get(leftChildID);
int rightChildID = getRightChildID(pos);
T smaller;
int smallerID;
if (rightChildID < size) {
T rightChild = heap.get(rightChildID);
if (leftChild.getRank() < rightChild.getRank()) {
smaller = leftChild;
smallerID = getLeftChildID(pos);
} else {
smaller = rightChild;
smallerID = getRightChildID(pos);
}
} else {
smaller = leftChild;
smallerID = getLeftChildID(pos);
}
if (smaller.getRank() < e.getRank()) {
heap.set(pos, smaller);
smaller.setHeapIdx(pos);
heap.set(smallerID, e);
siftDown(e, smallerID);
}
}
}
/**
* Gets the position of the second child in the array.
*
* @param pos
* The position of the parent.
* @return The position of the child.
*/
private final int getRightChildID(int pos) {
return 2 * pos + 2;
}
/**
* Gets the position of the first child in the array.
*
* @param pos
* The position of the parent.
* @return The position of the child.
*/
private final int getLeftChildID(int pos) {
return 2 * pos + 1;
}
/**
* Gets the position of the parent in the array.
*
* @param pos
* The position of the child.
* @return The position of the parent.
*/
private final int getParent(int pos) {
return (pos - 1) / 2;
}
@Override
public String toString() {
StringBuffer buffer = new StringBuffer();
buffer.append("\t");
for (int i = 0; i < size; i++) {
buffer.append(heap.get(i).getRank() + "\t");
}
return buffer.toString();
}
/**
* Clears the heap, that means that all elements are removed.
*/
public final void clear() {
size = 0;
}
/**
* Gets the number of elements that are in the heap.
*
* @return The size of the heap.
*/
public final int size() {
return size;
}
/**
* Removes a given element from the heap.
*
* @param e
* The element to remove
* @throws java.lang.AssertionError
*/
public void remove(T e) throws java.lang.AssertionError {
// int idx = getIndex(e, 0);
int idx = e.getHeapIdx();
// if (idx == -1)
// return;
assert idx != -1 : "remove wrong element";
size--;
T last = heap.get(size);
heap.set(idx, last);
if (!siftUp(last, idx)) {
siftDown(last, idx);
}
}
/**
* Checks the heap for consistency.
*
* @return True if all children "know" their position.
*/
public boolean doFullHeapCheck() {
for (int i = 0; i < size; i++) {
if (heap.get(i).getHeapIdx() != i) {
return false;
}
}
return checkHeap(0);
}
/**
* Checks the heap from a given position downwards.
*
* @param pos
* The position to start checking.
* @return true if and only if the heap is ordered the right way.
*/
private boolean checkHeap(int pos) {
T curr = heap.get(pos);
int left = getLeftChildID(pos);
int right = getRightChildID(pos);
if (left < size) {
if (heap.get(left).getRank() < curr.getRank()) {
return false;
} else {
return checkHeap(left);
}
}
if (right < size) {
if (heap.get(right).getRank() < curr.getRank()) {
return false;
} else {
return checkHeap(right);
}
}
return true;
}
}