import java.util.Scanner; import java.util.Arrays; import java.io.File; /* HeapSort.java ** This class has a method by which to rearrange the elements of an array of ** integers so that they end up in ascending order. It employs Williams's ** HeapSort algorithm. */ public class HeapSort { public static void sort(int[] A) { heapify(A); contractHeap(A); } /* Rearranges the elements in the given array so that, when interpreted ** as a binary tree, it is a max-heap (meaning that the value at each ** non-root node is less than or equal to the value in its parent node). */ private static void heapify(int[] A) { int N = A.length; // N/2 - 1 is the ID of the highest-numbered interior node for (int i = N/2 - 1; i >= 0; i = i-1) { siftDown(A,i,N); } } /* Rearranges the values in the given array, which is assumed to be ** a max-heap, so that they are in ascending order. */ private static void contractHeap(int[] A) { int N = A.length; for (int i = N-1; i != 0; i = i-1) { swap(A, 0, i); siftDown(A,0,i); } } /* Performs a siftdown operation within the max-heap represented ** by the array segment a[0..boundary), starting at node k. */ private static void siftDown(int[] a, int k, int boundary) { boolean keepGoing = true; while (keepGoing) { int largerChildLoc = locOfLargerChild(a, k, boundary); if (largerChildLoc == -1) { // Node k is a leaf keepGoing = false; } else if (a[k] >= a[largerChildLoc]) { // Node k's value is at least keepGoing = false; // as large as children's } else { swap(a, k, largerChildLoc); k = largerChildLoc; } } } /* Returns the ID of node k's "larger" child. (If node k has no children, ** -1 is returned.) Only locations [0..boundary) in ary[] are considered ** to be part of the max-heap. */ private static int locOfLargerChild(int[] ary, int k, int boundary) { int result; int locOfLeftChild = 2*k + 1; if (locOfLeftChild >= boundary) { result = -1; // Node k is a leaf (i.e., has no children) } else if (locOfLeftChild == boundary - 1) { result = locOfLeftChild; // Node k has only a left child } else { // Node k has both a left and a right child if (ary[locOfLeftChild] <= ary[locOfLeftChild + 1]) { result = locOfLeftChild + 1; } else { result = locOfLeftChild; } } return result; } /* Interchanges the values in the specified locations ** of the specified array. */ private static void swap(int[] b, int p, int q) { int temp = b[p]; b[p] = b[q]; b[q] = temp; } // main method (for testing purposes) // ---------------------------------- public static void main(String[] args) { // args[0] is taken to be the name of a file containing the numbers // to be sorted. The first number in the file should be how many // numbers follow. For example, a suitable file could look like this: // 10 6 12 -4 17 23 19 5 32 16 8 // The "10" indicates that (at least) ten numbers follow. // The numbers need not all be on the same line. int[] a = readArray(args[0]); System.out.println("Array to be sorted:"); System.out.printf(" %s\n", Arrays.toString(a)); sort(a); System.out.println("\nArray after sorting:"); System.out.printf(" %s\n", Arrays.toString(a)); } private static int[] readArray(String fileName) { int[] result = null; try { Scanner input = new Scanner(new File(fileName)); int N = input.nextInt(); // # of numbers in collection to be sorted result = new int[N]; for (int i=0; i != N; i++) { result[i] = input.nextInt(); } } catch (Exception e) { e.printStackTrace(System.out); System.out.println("\nProgram aborting..."); System.exit(0); } return result; } }