I tried to build a minHeap using java, this is my code:
public class MyMinHeap {
private ArrayList<Node> heap;
public MyMinHeap() {
heap = new ArrayList<Node>();
}
public MyMinHeap(ArrayList<Node> nodeList) {
heap = nodeList;
buildHeap();
}
public void buildHeap() {
int i = heap.size() / 2;
while (i >= 0) {
minHeapify(i);
i--;
}
}
public Node extractMin() {
if (heap.size() <= 0) return null;
Node minValue = heap.get(0);
heap.set(0, heap.get(heap.size() - 1));
heap.remove(heap.size() - 1);
minHeapify(0);
return minValue;
}
public String toString() {
String s = "";
for (Node n : heap) {
s += n + ",";
}
return s;
}
public void minHeapify(int i) {
int left = 2 * i + 1;
int right = 2 * i + 2;
int smallest = i;
if (left < heap.size() - 1 && lessThan(left, smallest))
smallest = left;
if (right < heap.size() - 1 && lessThan(right, smallest))
smallest = right;
if (smallest != i) {
swap(smallest, i);
minHeapify(smallest);
}
}
private void swap(int i, int j) {
Node t = heap.get(i);
heap.set(i, heap.get(j));
heap.set(j, t);
}
public boolean lessThan(int i, int j) {
return heap.get(i)
.compareTo(heap.get(j)) < 0;
}
public static void main(String[] args) {
char[] chars = {'a', 'b', 'c', 'd', 'e', 'f'};
int[] freqs = {45, 13, 12, 16, 9, 5};
ArrayList<Node> data = new ArrayList<Node>();
for (int i = 0; i < chars.length; i++) {
data.add(new Node(chars[i], freqs[i]));
}
MyMinHeap heap = new MyMinHeap(data);
System.out.println("print the heap : " + heap);
for (int i = 0; i < chars.length; i++) {
System.out.println("Smallest is :" + heap.extractMin());
}
}
}
The output should be:5,9,12,13,16,45,
but what I got is : 9,13,12,16,45
I have debugged this but still can't figure out, anybody help? thanks a lot.
Insert :
When we insert into a min-heap, we always start by inserting the element at the bottom. We insert at the
rightmost spot so as to maintain the complete tree property.
Then, we "fix" the tree by swapping the new element with its parent, until we find an appropriate spot for
the element. We essentially bubble up the minimum element.
This takes 0 (log n) time, where n is the number of nodes in the heap.
Extract Minimum Element :
Finding the minimum element of a min-heap is easy: it's always at the top. The trickier part is how to remove
it. (I n fact, this isn't that tricky.)
First, we remove the minimum element and swap it with the last element in the heap (the bottommost,
rightmost element). Then, we bubble down this element, swapping it with one of its children until the minheap
property is restored.
Do we swap it with the left child or the right child? That depends on their values. There's no inherent
ordering between the left and right element, but you'll need to take the smaller one in order to maintain
the min-heap ordering.
public class MinHeap {
private int[] heap;
private int size;
private static final int FRONT = 1;
public MinHeap(int maxSize) {
heap = new int[maxSize + 1];
size = 0;
}
private int getParent(int position) {
return position / 2;
}
private int getLeftChild(int position) {
return position * 2;
}
private int getRightChild(int position) {
return position * 2 + 1;
}
private void swap(int position1, int position2) {
int temp = heap[position1];
heap[position1] = heap[position2];
heap[position2] = temp;
}
private boolean isLeaf(int position) {
if (position > size / 2) {
return true;
}
return false;
}
public void insert(int data) {
heap[++size] = data;
int currentItemIndex = size;
while (heap[currentItemIndex] < heap[getParent(currentItemIndex)]) {
swap(currentItemIndex, getParent(currentItemIndex));
currentItemIndex = getParent(currentItemIndex);
}
}
public int delete() {
int item = heap[FRONT];
swap(FRONT, size--); // heap[FRONT] = heap[size--];
heapify(FRONT);
return item;
}
private void heapify(int position) {
if (isLeaf(position)) {
return;
}
if (heap[position] > heap[getLeftChild(position)]
|| heap[position] > heap[getRightChild(position)]) {
// if left is smaller than right
if (heap[getLeftChild(position)] < heap[getRightChild(position)]) {
// swap with left
swap(heap[position], heap[getLeftChild(position)]);
heapify(getLeftChild(position));
} else {
// swap with right
swap(heap[position], heap[getRightChild(position)]);
heapify(getRightChild(position));
}
}
}
#Override
public String toString() {
StringBuilder output = new StringBuilder();
for (int i = 1; i <= size / 2; i++) {
output.append("Parent :" + heap[i]);
output
.append("LeftChild : " + heap[getLeftChild(i)] + " RightChild :" + heap[getRightChild(i)])
.append("\n");
}
return output.toString();
}
public static void main(String... arg) {
System.out.println("The Min Heap is ");
MinHeap minHeap = new MinHeap(15);
minHeap.insert(5);
minHeap.insert(3);
minHeap.insert(17);
minHeap.insert(10);
minHeap.insert(84);
minHeap.insert(19);
minHeap.insert(6);
minHeap.insert(22);
minHeap.insert(9);
System.out.println(minHeap.toString());
System.out.println("The Min val is " + minHeap.delete());
}
}
The problem is in your minHeapify function. You have:
public void minHeapify(int i) {
int left = 2 * i + 1;
int right = 2 * i + 2;
int smallest = i;
if (left < heap.size() - 1 && lessThan(left, smallest))
smallest = left;
if (right < heap.size() - 1 && lessThan(right, smallest))
smallest = right;
Now, let's say that your initial array list is {3,2}, and you call minHeapify(0).
left = 2 * i + 1; // = 1
right = 2 * i + 2; // = 2
smallest = i; // 0
Your next statement:
if (left < heap.size() - 1 && lessThan(left, smallest))
At this point, left = 1, and heap.size() returns 2. So left isn't smaller than heap.size() - 1. So your function exits without swapping the two items.
Remove the - 1 from your conditionals, giving:
if (left < heap.size() && lessThan(left, smallest))
smallest = left;
if (right < heap.size() && lessThan(right, smallest))
smallest = right;
Related
I'm trying to implement a heapsort on an array of objects which has already been created. It is meant to be sorted according to their eventTime attribute, yet each eventTime attribute is set to the same as the others by the time the queue is sorted. Is there any way to fix this so that it creates a 'priorityqueue' in ascending order?
Main:
public static void main(String[] args) {
CustomerQueue cQ = new CustomerQueue(0, 0, false);
for (int i = 0; i < 10; i++) {
CustomerQueue cQ1 = new CustomerQueue(0, 0, false);
cQ.enqueue(cQ1);
System.out.println(cQ1.arrivalTime);
}
System.out.print("\n");
PriorityQueue pQ = new PriorityQueue(0, 0, 0, false);
while (!cQ.isEmpty()) {
for (CustomerQueue c : cQ.array) {
PriorityQueue pQ1 = new PriorityQueue(0, 0, 0, false);
pQ1.eventTime = c.arrivalTime;
pQ.enqueue(pQ1);
System.out.println(pQ1.eventTime);
cQ.dequeue();
}
}
pQ.sort(pQ.array);
System.out.println(Arrays.toString(pQ.array));
}
PriorityQueue:
class PriorityQueue {
PriorityQueue array[] = new PriorityQueue[10];
private int front;
private int rear;
private int count;
private int eventType;
double eventTime;
private double tallyTime;
private boolean paymentMethod;
public PriorityQueue(int evT, int eT, double tT, boolean pM) {
this.eventType = evT;
this.eventTime = eT;
this.tallyTime = tT;
this.paymentMethod = pM;
front = 0;
rear = -1;
count = 0;
}
public void enqueue(PriorityQueue pQ) {
if (isFull()) {
System.out.println("OverFlow\nProgram Terminated");
System.exit(1);
}
rear = (rear + 1);
array[rear] = pQ;
count++;
}
public void sort(PriorityQueue arr[]) {
int n = arr.length;
// Build heap (rearrange array)
for (int i = n / 2 - 1; i >= 0; i--) {
heapify(arr, n, i);
}
// One by one extract an element from heap
for (int i = n - 1; i >= 0; i--) {
// Move current root to end
int temp = (int) arr[0].eventTime;
arr[0] = arr[i];
arr[i].eventTime = temp;
// call max heapify on the reduced heap
heapify(arr, i, 0);
}
}
void heapify(PriorityQueue arr[], int n, int i) {
int largest = i; // Initialize largest as root
int l = 2 * i + 1; // left = 2*i + 1
int r = 2 * i + 2; // right = 2*i + 2
// If left child is larger than root
if (l < n && arr[l].eventTime > arr[largest].eventTime) {
largest = l;
}
// If right child is larger than largest so far
if (r < n && arr[r].eventTime > arr[largest].eventTime) {
largest = r;
}
// If largest is not root
if (largest != i) {
int swap = (int) arr[i].eventTime;
arr[i] = arr[largest];
arr[largest].eventTime = swap;
// Recursively heapify the affected sub-tree
heapify(arr, n, largest);
}
}
public void dequeue() {
if (isEmpty()) {
System.out.println("UnderFlow\nProgram Terminated");
System.exit(1);
}
front = (front + 1) % array.length;
count--;
}
public boolean isEmpty() {
return (size() == 0);
}
public int size() {
return count;
}
public boolean isFull() {
return (size() == array.length);
}
public PriorityQueue peek() {
if (isEmpty()) {
System.out.println("UnderFlow\nProgram Terminated");
System.exit(1);
}
return array[front];
}
#Override
public String toString() {
return eventType + " " + eventTime + " " + tallyTime + " " + paymentMethod;
}
}
I see two parts which are most likely wrong and the cause of your issue:
In the heapify method you have:
int swap = (int) arr[i].eventTime;
arr[i] = arr[largest];
arr[largest].eventTime = swap;
And in the sort method you have:
int temp = (int) arr[0].eventTime;
arr[0] = arr[i];
arr[i].eventTime = temp;
Assuming you want to switch the CustomerQueue objects around inside the array, they should be this instead:
// In the heapify:
CustomerQueue swap = arr[i];
arr[i] = arr[largest];
arr[largest] = swap;
// In the sort:
CustomerQueue temp = arr[0];
arr[0] = arr[i];
arr[i] = temp;
What your code currently does in pseudo-code is this:
CustomerQueue ith has for example name = "I", eventTime = 1
CustomerQueue largest has for example name = "largest", eventTime = 2
int temp = (int) arr[i].eventTime; // temp is now 1
arr[i] = arr[largest]; // Both arr[i] AND arr[largest] are now CustomerQueue largest (with name = "largest" and eventTime = 2)
arr[i].eventTime = temp; // Both arr[i] AND arr[largest] are now changed to eventTime temp (which is 1)
So now both arr[i] and arr[largest] will hold: name = "largest" and eventTime = 1, as well as referencing the same CustomerQueue-instance.
I am trying to insert in a maxHeap in java and then bubble up the object. This is what I have done, I am not sure how I should approach the bubble up method.
I do understand the algorithm behind bubble up, which is as follows:
get parent node
see if L_childNode is less than parent Node. If Yes, then swap parent with L_child.
see if R_childNode is less than parent Node. If Yes, then swap parent with L_child.
Please point out what am I doing wrong?
private int getLeftChild(int n){
return x*2+1;
}
private int getRightChild(int n){
return x*2+2;
}
public void insert (E item) {
//Integer pos_lastEl= new Integer (heapArray.lastElement());
heapArray.add(item);
bubbleUp(item);
}
//To use to reheap up when item inserted at end of heap (complete tree)
private void bubbleUp(E x){
int place = heapArray.size()-1;
int parent=(place-1)/2;
if ((parent>=0) && (parent.compareTo(heapArray.get(getLeftChild))<0)){
swap(place,parent);
}else ((parent>=0 && (parent.compareTo(heapArray.get(getRightChild))<0))){
swap(place,parent);
}
}
//swaps two objects at index i and j
private void swap(int i, int j){
int max=heapArray.size();
if(i>=0 && i<max && j>=0 && j<max){
E temp=heapArray.get(i);
//put J item in I
heapArray.set(i,heapArray.get(j));
heapArray.set(j,temp);
}
}
Your major problem is using if instead of while to bubble up the newly added element to the proper position.
And there are also some other issues in your code, sorry I had to do some refactoring to make it clean enough:
public class MaxHeapTest<E extends Comparable<E>> {
List<E> heapArray = new ArrayList<>();
public static void main(String... args) {
int N = 13;
MaxHeapTest<Integer> maxHeap = new MaxHeapTest();
for (int i = 0; i < N; ++i) { // ascending;
maxHeap.insert(i);
}
while (!maxHeap.isEmpty()) { // descending now;
System.out.print(maxHeap.delMax() + " ");
}
}
public E delMax() {
E e = heapArray.get(0);
swap(0, heapArray.size() - 1);
heapArray.remove(heapArray.size() - 1);
sinkDown(0);
return e;
}
public void insert(E item) {
heapArray.add(item);
bubbleUp(item);
}
public boolean isEmpty() {
return heapArray.isEmpty();
}
private void bubbleUp(E x) {
int k = heapArray.indexOf(x);
int j = (k - 1) / 2;
while (j >= 0) {
if (heapArray.get(j).compareTo(heapArray.get(k)) < 0) {
swap(k, j);
k = j;
j = (j - 1) / 2;
} else break;
}
}
private void sinkDown(int k) {
int j = 2 * k + 1;
while (j < heapArray.size()) {
if (j < heapArray.size() - 1 && heapArray.get(j).compareTo(heapArray.get(j + 1)) < 0) j++;
if (heapArray.get(k).compareTo(heapArray.get(j)) < 0) {
swap(k, j);
k = j;
j = 2 * j + 1;
} else break;
}
}
private void swap(int i, int j) {
E temp = heapArray.get(i);
heapArray.set(i, heapArray.get(j));
heapArray.set(j, temp);
}
}
After the maxHeap, we can easily output the descending numbers as:
12 11 10 9 8 7 6 5 4 3 2 1 0
Whats up guys, I have a question regarding the Dijkstra algorithm. I have made it so that a user enter a graph file, and then the user enters the source node and destination node. And my code so far calculates the shortest distance between them, like it's supposed to. However I do not know to print the path of nodes it goes through in order to get to the destination node. Please help this is a homework assignment due today. Here is my code:
package minheap;
import java.io.File;
import java.io.FileNotFoundException;
import java.util.Scanner;
public class MinHeap {
private int veNum;
private int x;
private int y;
private Vertex[] vertices;
private int size;
public static void main(String[] args) throws FileNotFoundException{
Scanner input = new Scanner(System.in);
System.out.println("%%%%%%Enter the name of the file that contains the graph");
String file = input.nextLine();
MinHeap minHeap = new MinHeap(file);
System.out.println("What city would you like to start from?(Please enter a number "
+ "from 0 to 87574)");
int start = Integer.parseInt(input.nextLine());
System.out.println("What city would you like to get to?(Please enter a number "
+ "from 0 to 87574)");
int end = Integer.parseInt(input.nextLine());
minHeap.findShortestPaths(start, end);
}
public MinHeap(String file) throws FileNotFoundException {
Scanner input = new Scanner(new File(file));
String sizeString = input.next(); //get the size as a string
size = Integer.parseInt(sizeString);
System.out.println("======The size is: " + size);
vertices = new Vertex[size];
// addNodes();
input.next();
/*Now read the vertices*/
for(int i = 0; i < vertices.length; i++){
veNum = Integer.parseInt(input.next());
x = Integer.parseInt(input.next());
y = Integer.parseInt(input.next());
vertices[i] = new Vertex(veNum, x, y);
}
/*Now read the edges */
while(input.hasNext()){
int vertex1 = indexForName(input.next());
int vertex2 = indexForName(input.next());
//System.out.println("====" + vertex1 + "====" + vertex2);
/*Formula to calculate the distance(weight)*/
int distance = (int) Math.sqrt(Math.pow(vertices[vertex1].xCord - vertices[vertex2].xCord, 2)
+ Math.pow(vertices[vertex1].yCord - vertices[vertex2].yCord, 2));
/*Pass the 2 vertexes that make an edge, and their distance to addEdge method*/
addEdge(vertex1, vertex2, distance);
/*System.out.println("X cord: " + vertices[vertex1].xCord + "| Y cord: " + vertices[vertex1].yCord +
"| X cord2: " + vertices[vertex2].xCord + "| Y cord2: " + vertices[vertex2].yCord);
*/
}
}
//======================================================================================================
int indexForName(String name){
for(int i = 0; i < vertices.length; i++){
/*Look for the vertex name in the array to see if they match with the one the one from
the file */
if(vertices[i].name == Integer.parseInt(name)){
return i;
}
}
return -1;
}
//===============================================================================================================
public void addEdge(int sourceName, int destinationName, int weight) {
int srcIndex = sourceName;
int destiIndex = destinationName;
vertices[srcIndex].adj = new Neighbour(destiIndex, weight, vertices[srcIndex].adj);
vertices[destiIndex].indegree++;
}
public void findShortestPaths(int sourceName, int end){
for (int i = 0; i < size; i++) {
if (vertices[i].name == sourceName) {
applyDikjstraAlgorith(vertices[i], vertices[end]);
break;// in this case we need not traverse the nodes which are
// not reachable from the source Node
}
}
//applyDikjstraAlgorith(vertices[sourceName], vertices[end]);
// for(int i = 0; i < size; i++){
// System.out.println("Distance of "+vertices[i].name+" from Source: "+ vertices[i].cost);
//}
}
public class Vertex {
int cost;
int name;
Neighbour adj;
int indegree;
State state;
int xCord;
int yCord;
public Vertex(int name, int xCord, int yCord) {
this.name = name;
cost = Integer.MAX_VALUE;
state = State.NEW;
this.xCord = xCord;
this.yCord = yCord;
}
public int compareTo(Vertex v) {
if (this.cost == v.cost) {
return 0;
}
if (this.cost < v.cost) {
return -1;
}
return 1;
}
}
public enum State {
NEW, IN_Q, VISITED
}
public class Neighbour {
int index;
Neighbour next;
int weight;
Neighbour(int index, int weight, Neighbour next) {
this.index = index;
this.next = next;
this.weight = weight;
}
}
public void applyDikjstraAlgorith(Vertex src, Vertex end) {
Heap heap = new Heap(size);
heap.add(src);
src.state = State.IN_Q;
src.cost = 0;
while (!heap.isEmpty()) {
Vertex u = heap.remove();
u.state = State.VISITED;
Neighbour temp = u.adj; //the neighbor of the vertex being removed. it accesses it adj neighbor list
System.out.println("=======Edge weights");
while (temp != null) { //while it has a neighbor
if (vertices[temp.index].state == State.NEW) { //if that neighbor is unvisited
heap.add(vertices[temp.index]); //add the unvisited vertices to the heap
vertices[temp.index].state = State.IN_Q; //make the state indicating its in the heap
}
System.out.println("Weight from "+ vertices[u.name].name + " to " + vertices[temp.index].name +" is "+ temp.weight);
if (vertices[temp.index].cost > u.cost + temp.weight) { //if the neighbors weight is less than
vertices[temp.index].cost = u.cost + temp.weight;
heap.heapifyUP(vertices[temp.index]);
}
temp = temp.next;
}
}
System.out.println();
System.out.println("The shortest distance from "+src.name +" to "+end.name+" is "
+ end.cost);
}
public static class Heap {
private Vertex[] heap;
private int maxSize;
private int size; //starts off as 0
public Heap(int maxSize) {
this.maxSize = maxSize;
heap = new Vertex[maxSize]; //make the max size for the heap array made of vertices
}
public void add(Vertex u) {
heap[size++] = u; //fill the heap array with the vertices, starting at position 0
//
heapifyUP(size - 1); //pass each vertext ino heapifyUP (vertex type)
}
public void heapifyUP(Vertex u) {
for (int i = 0; i < maxSize; i++) { //look for vertex in the heap array
if (u == heap[i]) {
heapifyUP(i); //if its found, go to heapifyUp method (int type) and pass in the vertex num
break;
}
}
}
public void heapifyUP(int position) {
int currentIndex = position;
Vertex currentItem = heap[currentIndex];
int parentIndex = (currentIndex - 1) / 2;
Vertex parentItem = heap[parentIndex];
while (currentItem.compareTo(parentItem) == -1) {
swap(currentIndex, parentIndex);
currentIndex = parentIndex;
if (currentIndex == 0) {
break;
}
currentItem = heap[currentIndex];
parentIndex = (currentIndex - 1) / 2;
parentItem = heap[parentIndex];
}
}
public Vertex remove() {
Vertex v = heap[0];
swap(0, size - 1);
heap[size - 1] = null;
size--;
heapifyDown(0);
return v;
}
public void heapifyDown(int postion) {
if (size == 1) {
return;
}
int currentIndex = postion;
Vertex currentItem = heap[currentIndex];
int leftChildIndex = 2 * currentIndex + 1;
int rightChildIndex = 2 * currentIndex + 2;
int childIndex;
if (heap[leftChildIndex] == null) {
return;
}
if (heap[rightChildIndex] == null) {
childIndex = leftChildIndex;
} else if (heap[rightChildIndex].compareTo(heap[leftChildIndex]) == -1) {
childIndex = rightChildIndex;
} else {
childIndex = leftChildIndex;
}
Vertex childItem = heap[childIndex];
while (currentItem.compareTo(childItem) == 1) {
swap(currentIndex, childIndex);
currentIndex = childIndex;
currentItem = heap[currentIndex];
leftChildIndex = 2 * currentIndex + 1;
rightChildIndex = 2 * currentIndex + 2;
if (heap[leftChildIndex] == null) {
return;
}
if (heap[rightChildIndex] == null) {
childIndex = leftChildIndex;
} else if (heap[rightChildIndex].compareTo(heap[leftChildIndex]) == -1) {
childIndex = rightChildIndex;
} else {
childIndex = leftChildIndex;
}
}
}
public void swap(int index1, int index2) {
Vertex temp = heap[index1];
heap[index1] = heap[index2];
heap[index2] = temp;
}
public boolean isEmpty() {
return size == 0;
}
}
}
The sample.txt file is as follows:
6 9
0 1000 2400
1 2800 3000
2 2400 2500
3 4000 0
4 4500 3800
5 6000 1500
0 1
0 3
1 2
1 4
2 4
2 3
2 5
3 5
Again, the actual algorithm works, I just need help printing the path. Thanks in advance
Current I have a home work question which says,
It is possible to make the heap sort algorithm more efficient by
writing a method that will order the entire list at once instead of
adding the elements one at a time.
However I can't figure out what exactly it means by "instead of adding elements one at a time", surely one has to building a heap first (which involves adding element from a unsorted list one by one), then remove the largest from the heap one at a time.
Here is my heap array:
import exceptions.exceptions.*;
public class ArrayHeap<T> extends ArrayBinaryTree<T> implements HeapADT<T> {
public ArrayHeap(){
super();
}
public void addElement (T element){
if (count==size())
expandCapacity();
tree[count] = element;
count++;
if (count > 1)
heapifyAdd();
}
private void heapifyAdd(){
int index = count - 1;
while ((index != 0) && (((Comparable)tree[index]).compareTo(tree[(index-1)/2]) < 0))
{
T temp = tree[index];
tree[index] = tree[(index-1)/2];
tree[(index-1)/2] = temp;
index = (index-1)/2;
}
}
public T removeMin(){
if (isEmpty())
throw new EmptyCollectionException ("Empty Heap");
T minElement = findMin();
tree[0] = tree[count-1];
heapifyRemove();
count--;
return minElement;
}
private void heapifyRemove()
{
T temp;
int node = 0;
int left = 1;
int right = 2;
int next;
if ((tree[left] == null) && (tree[right] == null))
next = count;
else if (tree[left] == null)
next = right;
else if (tree[right] == null)
next = left;
else if (((Comparable)tree[left]).compareTo(tree[right]) < 0)
next = left;
else
next = right;
while ((next < count) && (((Comparable)tree[next]).compareTo(tree[node]) < 0)){
temp = tree[node];
tree[node] = tree[next];
tree[next] = temp;
node = next;
left = 2*node + 1;
right = 2*(node+1);
if ((tree[left] == null) && (tree[right] == null))
next = count;
else if (tree[left] == null)
next = right;
else if (tree[right] == null)
next = left;
else if (((Comparable)tree[left]).compareTo(tree[right]) < 0)
next = left;
else
next = right;
}
}
public T findMin() {
if (isEmpty())
throw new EmptyCollectionException ("Empty Heap");
return tree[0];
}
}
Here is more HeapSort algorithm:
import ArrayHeap;
public class HeapSort<T>{
public T[] heapsort(T[] data, int min, int max){
ArrayHeap<T> temp = new ArrayHeap<T>();
for (int c = min; c <= max; c++){
temp.addElement(data[c]);
}
int count = min;
while(!(temp.isEmpty())){
T jj = temp.removeMin();
data[count] = jj;
count ++;
}
return data;
}
The most straight-forward way to perform heapsort is to use a separate heap and add all the elements to it, then the elements will be in order when we pop them out one by one. This is what "adding the elements one at a time" refers to in the statement, and this is what your implementation is doing: create a heap of type ArrayHeap and insert the elements of data to it, in the end pop the elements back to data.
A more efficient way (in terms of both space and time) is to perform in-place sorting, where we use the array to be sorted as the heap, rather than using additional memory for the heap, this is what "order the entire list at once" refers to. The steps of this implementation is as follow, we will order the elements in non-decreasing order:
We max-heapify the input array (i.e. we re-arrange the elements in the array so that it follows the max-heap property.
For i = n - 1 to 1:
Swap the 0-th element in the array with the i-th element.
Decrease the size of the heap by 1 (i.e. the heap should be of size i).
Perform the sift-down operation on the heap to restore the max-heap property.
Note that whenever the max-heap property holds, the top-most element in the heap is the largest element, so at the start of the k-th iteration (k = n - i here) the 0-th element is the k-largest element, and we place is in the correct position in the array by swapping.
Note that step 1 can be done in O(n), and in step 2 there are O(n) iterations and each sift-down operation takes time O(log(n)), so the overall time complexity is O(n log(n)).
Below is an implementation in Java for your reference:
import java.util.Random;
public class HeapSort {
public static void main(String[] args) {
for (int i = 1; i <= 10; i++) {
System.out.println(String.format("Iteration number %d%n", i));
Integer[] array = randomIntArray(10, 0, 100);
System.out.println(String.format("Array before sorting: [%s]", toStr(array)));
heapSort(array);
System.out.println(String.format("Array after sorting: [%s]", toStr(array)));
System.out.println("================================================================");
}
}
private static <T extends Comparable<T>> T[] heapSort(T[] array) {
maxHeapify(array, array.length);
for (int i = array.length - 1; i > 0; i--) {
swap(array, 0, i);
siftDown(array, i, 0);
}
return array;
}
private static <T extends Comparable<T>> void maxHeapify(T[] array, int heapSize) {
for (int i = getParentIdx(heapSize - 1); i >= 0; i--) {
siftDown(array, heapSize, i);
}
}
private static <T extends Comparable<T>> void siftDown(T[] array, int heapSize, int idx) {
final int length = Math.min(array.length, heapSize) - 1;
if (idx > length || idx < 0) throw new IllegalArgumentException("Index out of range");
while (true) {
int maxIdx = idx;
int leftChildIdx = getLeftChildIdx(idx);
int rightChildIdx = getRightChildIdx(idx);
if (leftChildIdx <= length && array[maxIdx].compareTo(array[leftChildIdx]) < 0) maxIdx = leftChildIdx;
if (rightChildIdx <= length && array[maxIdx].compareTo(array[rightChildIdx]) < 0) maxIdx = rightChildIdx;
if (idx != maxIdx) {
swap(array, idx, maxIdx);
idx = maxIdx;
} else {
return;
}
}
}
private static int getParentIdx(int idx) {
return (idx - 1) / 2;
}
private static int getLeftChildIdx(int idx) {
return idx * 2 + 1;
}
private static int getRightChildIdx(int idx) {
return idx * 2 + 2;
}
private static <T> void swap(T[] array, int i, int j) {
T tmp = array[i];
array[i] = array[j];
array[j] = tmp;
}
private static <T> String toStr(T[] array) {
StringBuilder sb = new StringBuilder();
for (T element : array) {
sb.append(element + ", ");
}
return sb.substring(0, sb.length() - 2);
}
private static Integer[] randomIntArray(int size, int lowerBound, int upperBound) {
Integer[] result = new Integer[size];
Random random = new Random();
int diff = upperBound - lowerBound + 1;
for (int i = 0; i < size; i++) result[i] = lowerBound + random.nextInt(diff);
return result;
}
}
package x;
public class MaxHeap {
private Element[] heapArray;
private int maxSize;
private int currentSize;
public MaxHeap(int max) {
maxSize = max;
currentSize = 0;
heapArray = new Element[maxSize]; // create the heap
}
public boolean isEmpty() {
return currentSize == 0;
}
// Move an element up in the heap tree.
public void adjustHeap(int index) {
int parent = (index - 1) / 2;
Element bottom = heapArray[index];
while (index > 0 && heapArray[parent].getData() < bottom.getData()) {
heapArray[index] = heapArray[parent]; // move it down
index = parent;
parent = (parent - 1) / 2;
}
heapArray[index] = bottom;
}
public boolean insert(int key) {
if (currentSize == maxSize)
return false;
Element newElement = new Element(key);
heapArray[currentSize] = newElement;
adjustHeap(currentSize++);
return true;
}
public Element[] getMaxHeap() {
return heapArray;
}
public void printHeap() {
int i;
for (i = 0; i < maxSize; i++)
System.out.print(heapArray[i].getData() + " ");
System.out.println();
}
public void deleteMax() {
heapArray[0].setData(heapArray[maxSize - 1].getData());
currentSize--;
int i = 0;
while (i<=heapArray[maxSize - 1].getData()) {
int left = 2 * i + 1;
int right = 2 * i + 2;
if (heapArray[left].getData() <= heapArray[right].getData()) {
i = (2 * i + 1);
adjustHeap(right);
i++;
}
if (heapArray[left].getData() >= heapArray[right].getData()) {
i = (2 * i + 2);
adjustHeap(left);
i++;
}
}
}
}
package x;
class Element {
private int inData;
public Element(int data){
inData = data;
}
public int getData(){
return inData;
}
public void setData(int data){
inData = data;
}
}
package x;
public class Test {
/**
* #param args
*/
public static void main(String[] args) {
// TODO Auto-generated method stub
MaxHeap heap = new MaxHeap(13);
heap.insert(2);
heap.insert(20);
heap.insert(10);
heap.insert(5);
heap.insert(6);
heap.insert(15);
heap.insert(7);
heap.insert(8);
heap.insert(18);
heap.insert(11);
heap.insert(4);
heap.insert(3);
heap.insert(1);
heap.printHeap();
System.out.println("\n");
heap.deleteMax();
heap.printHeap();
}
}
My question is: Why is my while loop only executing one time? I want my deleteMax method to delete the max node (the root) then properly sort the heap so that it again is a max heap. the max heap is the following:
20 18 15 8 11 10 7 2 6 5 4 3 1
once deleteMax is called it should output:
18 11 15 8 5 10 7 2 6 1 3 4
but it instead outputs:
18 1 15 8 11 10 7 2 6 5 4 3 1
clearly it is deleting the max node, replacing it with the bottom most leaf, and swapping the new root with its largest child. It should then go on to swap 1 down the heap, but it stops before doing so.
I have tried lots of different logic for the while loop including:
while(Math.floor(i/2) <= 2*i+1 && Math.floor(i/2) <= 2*i+2)
and increasing i but nothing seems to work.
You're not rotating the root. Your loop body should look something like this.
if (heapArray[left].getData() < heapArray[i].getData()) {
temp = heapArray[i].getData();
heapArray[i].setData(heapArray[left].getData());
heapArray[left].setData(temp);
i = (2 * i + 1);
}
else if (heapArray[right].getData() > heapArray[i].getData()) {
temp = heapArray[i].getData();
heapArray[i].setData(heapArray[right].getData());
heapArray[right].setData(temp);
i = (2 * i + 2);
}
Your adjustHeap method might already be performing this swap, I couldn't be sure. The bottom line is that you need to rotate your root through the heap (rather than comparing branches), and you shouldn't have the i++ statement in there. It should also be < instead of <= and > instead of >=, but that's just a minor optimization.