All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.anc.util.Heap Maven / Gradle / Ivy

The newest version!
/*-
 * Copyright 2009 The American National Corpus
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 *
 */
package org.anc.util;

import java.lang.reflect.Array;
import java.util.AbstractCollection;

/**
 * An array based heap data structure.
 * 

* A heap data structure is (conceptually) a complete binary tree that obeys two * properties: *

    *
  1. The smallest (or largest) item in the heap is contained in the root node * of the tree. *
  2. All sub-trees in the heap are themselves heaps. *
*

* What is meant by "smallest" and "largest" depends on the object and how it * implements the Comparable interface. *

* Heaps can be used to retrieve an object with the largest (or smallest) key * from a collection in O(log n) time, where n is the * number of objects in the heap. Another nice feature of heaps is that they can * be efficiently (and elegantly) implemented with arrays. If we use a one-based * array, that is the first element is heap[1], then given an element at * heap[n] we know that the element's parent is in * heap[n/2], its left child is in heap[2n] and its * right child is in heap[2n + 1]. *

* Since the Heap uses a one-based array and Java uses zero-based arrays the * first element of the array is simply ignored. *

* The size of the heap is dynamic and doubles in size when more space is * needed. This means that the heap actualy performs in ammortized * O(log n) time, that is, every nth insert will * require a O(n) resize operation. * * @author Keith Suderman * @version 1.0 * */ public class Heap> extends AbstractCollection { /** Initial size of the heap */ private static final int INITIAL_SIZE = 16; /** The number of objects currently on the heap. */ protected int size = 0; /** * The number of objects the heap can hold before it needs to be resized. The * heap's capacity doubles every time it needs to resize. */ protected int capacity; /** The heap's storage space. */ protected Object[] heap = null; /** Default constructor. */ public Heap() { this(INITIAL_SIZE); } /** * Constructor that permits the initial size of the heap to be specified. * * @param initialSize * The initial size of the heap. */ public Heap(int initialSize) { capacity = initialSize; heap = new Object[capacity]; } /** * Copy constructor. Note, this contructor only performs a shallow copy of * the array used to store the heap. The items contained in the heap are not * copied or cloned. Changing the elements in the copied heap will result in * changes to the element of the original heap. * * @param other * The heap that will be copied. */ @SuppressWarnings("unchecked") public Heap(Heap other) { size = other.size; capacity = other.capacity; heap = (T[]) Array.newInstance(other.peek().getClass(), capacity); System.arraycopy(other.heap, 0, heap, 0, size); } /** * Creates and returns an Iterator for traversing the underlying array. The * iterator does a linear traversal of the array, this returns the values in * the heap in a different order than is obtained by repeatedly calling * {@link #remove}. * * @return An Iterator that iterates over the underlying array. */ @Override public java.util.Iterator iterator() { return new Iterator(); } /** * Returns the number of objects in the heap. This is not the size of the * array used to represent the heap. * * @return The number of objects in the heap. */ @Override public int size() { return size; } /** * Adds the object to the heap and reorders the heap to obey the heap * properties. The object is added to the end of the array and then moved up * to it's proper location. *

* Every Nth insert (where N is the size of the array used to store the heap) * will cause the array to be resized and result in a O(N) cost for that * insert. However, this still gives us an ammortized O(log N) cost for all * inserts. * * @param value * The value to be added to the heap. */ @Override public boolean add(T value) { if (size + 1 >= capacity) { grow(); } ++size; heap[size] = value; this.bubbleUp(); return true; } /** * Removes and returns the object at the top of the heap. To rebalance the * heap the last element is moved to the top of the heap and then moved back * down to its proper location. *

* Since the array is never resized when objects are removed all removals can * be performed in O(log N) time. * * @return The object at the top of the heap. */ @SuppressWarnings("unchecked") public T remove() { if (0 == size) { return null; } T result = (T) heap[1]; heap[1] = heap[size]; --size; this.bubbleDown(1); return result; } /** * Peeks at the object on top of the heap. The object is not removed from the * heap. * * @return The object at the top of the heap, or null if the heap is empty. */ @SuppressWarnings("unchecked") public T peek() { if (0 == size) { return null; } return (T) heap[1]; } /** * Test if there are any objects on the heap. * * @return true if the heap is empty, false otherwise. */ @Override public boolean isEmpty() { return 0 == size; } /** * Remove all elements from the heap. All elements of the array are set to * null so the objects they referred to can be garbage collected. */ @Override public void clear() { // Null all the references in the heap so they can be garbage collected. for (int i = 1; i <= size; ++i) { heap[i] = null; } size = 0; } /** * Doubles the size of the array used to store the heap elements. */ protected void grow() { capacity += capacity; Object[] temp = new Object[capacity]; // T[] temp = (T[]) Array.newInstance(heap[0].getClass(), capacity); System.arraycopy(heap, 0, temp, 0, heap.length); heap = temp; } /** * Swaps the two objects at heap[e1] and heap[e2]. * * @param e1 * The array index of the first element. * @param e2 * The array index of the second element. */ protected void swap(int e1, int e2) { Object temp = heap[e1]; heap[e1] = heap[e2]; heap[e2] = temp; } /** * Takes the value stored at the end of the array and moves it up to its * proper location in the array. *

* The element at the end of the array is compared to its parent and if it * compares "less than", that is * comparator.compare(element, parent) < 0, the two elements are * swapped. The element continues to bubble up the heap until either the root * of the heap is reached, or * comparator.compare(element, parent) >= 0. * */ protected void bubbleUp() { int current = size; int parent = current / 2; while (parent > 0) { if (compareElements(current, parent) < 0) { swap(current, parent); current = parent; parent = current / 2; } else { break; } } } /** * Takes the value from the subtree rooted at heap[node] and * moves it down to its proper location in the subtree. First the smallest * child is found and the value at heap[node] is compared to the * smaller of the two children. If the value at heap[node] is * smaller the two values are swapped and bubbleDown is called on that * subtree. * * @param node * The root of the subtree. */ protected void bubbleDown(int node) { int lchild = node + node; int rchild = lchild + 1; if (lchild > size) { // There is no left child, which implies there is no right child, // which means we have reached the bottom of the tree and we can't // bubble down this value any farther. return; } if (rchild > size) { // There is no right child, so we only need to test the left child. if (compareElements(lchild, node) < 0) { // The left child contains a smaller value so swap them and rebalance // the subtree. swap(node, lchild); this.bubbleDown(lchild); } } else { // Find which subtree contains the smallest value, assume its the // right subtree to start int n = rchild; if (compareElements(lchild, rchild) < 0) { // Nope, left subtree contains the smallest value n = lchild; } // If the current node is smaller that the smallest child then swap // the nodes and continue to bubble down if (compareElements(n, node) < 0) { swap(n, node); this.bubbleDown(n); } } } /** * Prints the contens of the heap to System.out. Can be used for debugging * purposes. */ protected void dump() { System.out.println("Heap dump"); for (int i = 1; i <= size; ++i) { System.out.println("" + i + " : " + heap[i].toString()); } } /** * Compares two objects on the heap. If a Comparator object was provided it * will be used to compare the objects on the heap, otherwise it is assumed * that the object implement the Comparable interface. *

* If no Comparator has been provided and the objects do not implement the * Comparable interface it is simply assumed the objects are "equal". * * @param left * The array index to the object on the left hand side of the * comparison. * @param right * The array index to the object on the right hand side of the * comparison. * @return 0 if the objects are either not comparable with one another, or * they compare as equal. -1 if the object at heap[left] * compares smaller than heap[right] or 1 (one) * otherwise. */ @SuppressWarnings("unchecked") protected int compareElements(int left, int right) { T lhs = (T) heap[left]; T rhs = (T) heap[right]; return lhs.compareTo(rhs); } /** * Iterator class for the Heap. Note that the Heap Iterator simply performs a * linear traversal of the backing array. This results in the objects in the * heap being accessed in a different order than repeatedly calling * {@link #remove}. In particular, the elements in the heap will not * be accessed in the order specified by the comparator. * * @author Keith Suderman * @version 1.0 */ class Iterator implements java.util.Iterator { private int pointer = 1; public Iterator() { } @Override public boolean hasNext() { return pointer <= size; } @Override @SuppressWarnings("unchecked") public T next() { T result = (T) heap[pointer]; ++pointer; return result; } @Override public void remove() { throw new UnsupportedOperationException( "Objects in a heap can not be remove via an Iterator object."); } } } //class TestComparer implements java.util.Comparator //{ // public int compare(Object lhs, Object rhs) // { // int lvalue = ( (Integer) lhs).intValue(); // int rvalue = ( (Integer) rhs).intValue(); // return lvalue - rvalue; // } //}





© 2015 - 2024 Weber Informatics LLC | Privacy Policy