//<PLAINTEXT>
package Trees;

import java.util.*;

/**
 * Heap.java
 *
 *
 * Created: Thu Apr 10 16:02:19 2003
 *
 * @author <a href="mailto:shapiro@cse.buffalo.edu">Stuart C. Shapiro</a>
 * based on Riley Sect. 9.7
 */

public class Heap extends ArrayList {
    /**
     * Creates a new empty <code>Heap</code>.
     *
     */
    public Heap (){
	super();
    }

    /**
     * Creates a new <code>Heap</code> containing all the elements of
     * the array <code>a</code> value
     *
     * @param a the <code>int[]</code> whose elements are to be the
     * elements of this heap.
     */
    public Heap (int[] a){
	super();
	for (int i = 0; i < a.length; i++) {
	    add(new Integer(a[i]));
	}
	makeHeap();
    }

    /**
     * Makes this ArrayList into a true Heap.
     *
     */
    public void makeHeap() {
	for (int next = 1; next < size(); next++) {
	    promote(next);
	}
    }

    /**
     * Promotes the value in position ndx of the array representation
     * of this Heap up to its rightful position in its branch.
     *
     */
    public void promote(int ndx) {
	Comparable temp = (Comparable)get(ndx);
	int insertionPoint = ndx;
	int j = (ndx - 1) / 2; // points to parent
	while (j > 0 && temp.compareTo(get(j)) > 0) {
	    set(insertionPoint, get(j));
	    insertionPoint = j;
	    j = (j - 1) / 2;
	} // end of while (j > 0 && temp.compareTo(get(j)) > 0)
	// j == 0 || temp.compareTo(get(j)) < 0
	if (temp.compareTo(get(j)) < 0) {
	    set(insertionPoint, temp);
	} else { // j == 0 && temp.compareTo(get(0)) > 0
	    set(insertionPoint, get(0));
	    set(0, temp);
	} // end of else
    }

    /**
     * Inserts <code>obj</code> into its proper position in this heap.
     *
     * @param obj the <code>Comparable</code> object to be inserted.
     */
    public void insert(Comparable obj) {
	add(obj);
	promote(size()-1);
    }

    /**
     * Returns the first (greatest) element of this heap.
     *
     * @return the first (greatest) element of this heap.
     */
    public Comparable first() {
	return (Comparable)get(0);
    }

    /**
     * Deletes and returns the first element of this heap.
     *
     * @return the deleted element of this heap.
     */
    public Comparable delete() {
	Comparable oldFirst = first();
	int oldLast = size()-1;
	if (oldLast > 0) {
	    Comparable temp = (Comparable)get(oldLast);
	    int insertionPoint = 0;
	    int maxPointer = indexOfMax(insertionPoint, oldLast);
	    while (maxPointer<oldLast && temp.compareTo(get(maxPointer)) < 0) {
		set(insertionPoint, (Comparable)get(maxPointer));
		insertionPoint = maxPointer;
		maxPointer = indexOfMax(insertionPoint, oldLast);
	    } // end of while (indexOfMax<oldLast && temp.compareTo(get(maxPointer)) < 0)
	    set(insertionPoint,temp);
	} // end of if (oldLast > 0)
	remove(oldLast);
	return oldFirst;
    }

    /**
     * Returns the index of the maximum of the following elements of
     * this heap:
     * 1) the element pointed to by tempPointer;
     * 2) the left child of parent, if it exists;
     * 3) the right child of parent, if it exists.
     *
     * @param parent an <code>int</code> value
     * @param tempPointer an <code>int</code> value
     * @return the index of the element that may be put into parent's
     * position.
     */
    private int indexOfMax(int parent, int tempPointer) {
	if (2*parent+1 >= size()) {
	    // parent is leaf
	    return tempPointer;
	} // end of if (2*parent+1 >= size())
	 if (2*parent+2 >= size()) {
	     // parent only has left child
	     return maxPointer(2*parent+1, tempPointer);
	} // end of if (2*parent+2 >= size())
	 // parent has two children
	 return maxPointer(2*parent+1, maxPointer(2*parent+2, tempPointer));
    }

    /**
     * Returns whichever index, i or j, points to the larger element
     * of this heap.
     *
     * @param i an <code>int</code> value
     * @param j an <code>int</code> value
     * @return whichever index, i or j, points to the larger element
     * of this heap.
     */
    private int maxPointer(int i, int j) {
	return ((Comparable)get(i)).compareTo((Comparable)get(j)) > 0 ? i : j;
    }

    /**
     * Returns a <code>String</code> representation of this heap,
     * whose last node is in index max, starting at index i.
     *
     * @param i the index of the root of this subheap.
     * @param max the index of the last node in this heap.
     * @return a <code>String</code> representation of this heap,
     * whose last node is in index max, starting at index i.
     */
    public String heapString(int i, int max) {
	if (i > max) {
	    return "()";  // empty
	} // end of if (i > max)
	if (2*i+1 > max) {
	    return "(" + get(i) + ")"; // leaf
	} // end of if (2*i+1 > max)
	return "(" + get(i) + " "
	    + heapString(2*i+1, max) + " "
	    + heapString(2*i+2, max) + ")";
    }

    /**
     * Returns a <code>String</code> representation of this heap.
     *
     * @return a <code>String</code> representation of this heap.
     */
    public String toString() {
	return heapString(0, size()-1);
    }

    /**
     * Uses heap sort to sort an integer array.
     *
     * @param a the <code>int[]</code> to be sorted.
     */
    public static void heapSort(int[] a) {
	/* Heap sort can really be done in place.
	 * This is just an example. */
	Heap heap = new Heap(a);
	for (int i = a.length-1; i>=0; i--) {
	    a[i] = ((Integer)heap.delete()).intValue();
	}
    }

    /**
     * Returns a <code>String</code> representation of the arrray <code>a</code>.
     *
     * @param a the <code>int[]</code> whose <code>String</code>
     * representation is to be returned.
     * @return a <code>String</code> representation of the arrray <code>a</code>.
     */
    public static String arrayToString (int[] a) {
	String result = "[";
	for (int i=0; i<a.length-1; i++) {
	    result += a[i] + ",";
	} // end of for (int i=0; i<a.length; i++)
	return result + a[a.length-1] + "]";
    }

    /**
     * Tests the Heap class.
     *
     * @param args a <code>String[]</code> value
     */
    public static void main (String[] args) {
	int[] a = {2,3,6,1,4,8,0,7};
	System.out.println ("Array: " + arrayToString(a));
	Heap heap = new Heap(a);
	System.out.println ("Heap: " + heap);
	heap.insert(new Integer(5));
	System.out.println ("After inserting 5: " + heap);
	System.out.println("The first element is " + heap.first());
	System.out.println("The deleted first element :" + heap.delete());
	System.out.println("After deleting the first element :" + heap);
	heapSort(a);
	System.out.println("Sorted Array: " + arrayToString(a));
    } // end of main ()
    
    
}// Heap
