Search code examples
javaheapsort

Implementing Heap Sort from Scratch


import java.util.Arrays;

import javax.management.RuntimeErrorException;

public class MyHeap {

public static void main(String args) {
        MyHeap minHeap = new MyHeap(10, true);
        minHeap.insert(70);
        minHeap.insert(20);
        minHeap.insert(90);
        minHeap.insert(60);
        minHeap.insert(40);
        minHeap.insert(10);
        minHeap.insert(50);
        minHeap.insert(30);
        minHeap.insert(80);
        minHeap.insert(100);

        System.out.println("Min Heap: " + Arrays.toString(minHeap.heap));
    
        MyHeap maxHeap = new MyHeap(10, false);
        maxHeap.insert(70);
        maxHeap.insert(20);
        maxHeap.insert(90);
        maxHeap.insert(60);
        maxHeap.insert(40);
        maxHeap.insert(10);
        maxHeap.insert(50);
        maxHeap.insert(30);
        maxHeap.insert(80);
        maxHeap.insert(100);
    
        System.out.println("Max Heap: " + Arrays.toString(maxHeap.heap));
    }
    
    private int[] heap;
    private int size = 0;
    private int capacity;
    private boolean reverse;// true if min heap
    
    public MyHeap(int capacity) {
        this.capacity = capacity;
        heap = new int[capacity];
        // max heap by default
        this.reverse = false;
    }
    
    public MyHeap(int capacity, boolean reverse) {
        this.capacity = capacity;
        heap = new int[capacity];
        this.reverse = reverse;
    }
    
    private int compare(int a, int b) {
        if (reverse)
            return b - a;
        return a - b;
    }
    
    public void insert(int key) {
        if (isFull()) {
            throw new IndexOutOfBoundsException("Out of given capacity");
        }
        heap[size] = key;
        swim(size++);
    }
    
    // private void rightshift(int parent, int position) {
    // for (int i = position; i > parent; i--) {
    // if (compare(heap[i], heap[i - 1]) > 0)
    // swap(i, i - 1);
    // }
    // }
    
    private void swim(int position) {
        int parent = (position) / 2;
        if (position > 0 && compare(heap[parent], heap[position]) < 0) {
            swap(parent, position);
            // rightshift(parent, position);
            swim(parent);
        }
    }
    
    private void sink(int position) {
        int left = position * 2 + 1;
        int right = position * 2 + 2;
        int largest = position;
        if (left < size && compare(heap[position], heap[left]) < 0) {
            largest = left;
        }
        if (right < size && compare(heap[position], heap[right]) < 0) {
            largest = right;
        }
        if (largest != position) {
            swap(largest, position);
            sink(largest);
        }
    
    }
    
    public int getMin() {
        if (isEmpty()) {
            throw new IllegalStateException("No Data available");
        }
        if (reverse) {
            return heap[size - 1];
        }
        return heap[0];
    }
    
    public int getMax() {
        if (isEmpty()) {
            throw new IllegalStateException("No Data available");
        }
        if (reverse) {
            return heap[0];
        }
        return heap[size - 1];
    }
    
    public void delTop() {
        if (isEmpty()) {
            throw new IllegalStateException("No Data Available to delete");
        }
        swap(0, size - 1);
        size--;
        sink(0);
    }
    
    public void delMin() {
        if (isEmpty()) {
            throw new IllegalStateException("No Data Available to delete");
        }
        if (reverse) {
            throw new RuntimeErrorException(null, "You Initialized a Min heap");
        }
        swap(getMin(), 0);
        size--;
        sink(0);
    }
    
    public void delMax() {
        if (isEmpty()) {
            throw new IllegalStateException("No Data Available to delete");
        }
        if (!reverse) {
            throw new RuntimeErrorException(null, "You Initialized a Max heap");
        }
        swap(getMax(), 0);
        size--;
        sink(0);
    }
    
    public int size() {
        return size - 1;
    }
    
    public boolean isFull() {
        return size - 1 == capacity;
    }
    
    public boolean isEmpty() {
        return size == 0;
    }
    
    private void swap(int i, int j) {
        int temp = heap[i];
        heap[i] = heap[j];
        heap[j] = temp;
    }
    
    @Override
    public String toString() {
        int[] items = Arrays.copyOf(heap, size);
        return Arrays.toString(items);
    }

}


Output:

Min Heap: 10, 20, 40, 30, 80, 60, 70, 50, 90, 100

Max Heap: 100, 90, 80, 60, 70, 10, 50, 30, 20, 40

Problem Description:

I'm working on implementing a min-heap and max-heap in Java, but I'm encountering issues with sorting the array correctly. Let me explain the problem through an example:

Let's say we have an array arr initialized as follows: {0, 0, 0} (max heap).

We insert 70, and arr becomes {70, 0, 0}. No further changes will be done. We insert 50, and arr becomes {70, 50, 0}. No further changes will be done. We insert 80, and arr becomes {70, 50, 80}. Here, 80 is greater than its parent (70), so we swap them. After this swap, arr becomes {80, 50, 70}. Now, the array is still not sorted correctly. I attempted to solve this issue by introducing a rightshift function, but it didn't work as expected.

What I've Tried:

I have already implemented the basic structure of the min-heap and max-heap, but I believe the issues are primarily related to the swim, sink, and rightshift functions.

I have made changes to the code, including adjusting the compare function and modifying the swim and sink functions. However, the array is still not sorted as expected.

Question:

I need help fixing the sink, swim, and rightshift functions or any other necessary changes to ensure that the array is correctly sorted for both min-heap and max-heap implementations. Can you please review my code and provide guidance on what needs to be changed to achieve this?


Solution

  • Now, the array is still not sorted correctly.

    A heap is not a sorted array, nor is heapsort a matter of just building a heap.

    This also means you cannot have both getMin and getMax functions. You can only get the "top" of the heap as a reliable value. The last value in the array is not guaranteed to be the other extreme. For the same reason you cannot have both delMin and delMax functions, only a delTop function, like this:

        public void delTop() {
            if (isEmpty()) {
                throw new IllegalStateException("No Data Available to delete");
            }
            swap(0, --size);
            sink(0);
        }
    

    Heapsort consists of building a heap, and then repeatedly deleting the top from a heap that reduces in size and putting the extracted values in a sorted partition (at the end).

    Bugs

    Aside from the above remarks, there is one mistake in this part of the sink function:

            if (left < size && compare(heap[position], heap[left]) < 0) {
                largest = left;
            }
            if (right < size && compare(heap[position], heap[right]) < 0) {
                largest = right;
            }
    

    The second if condition compares the entry at position with the one at right, but it should compare the entry at largest. So correct to:

            if (left < size && compare(heap[position], heap[left]) < 0) {
                largest = left;
            }
            if (right < size && compare(heap[largest], heap[right]) < 0) {
                largest = right;
            }
    

    There is also an issue in the functions size() and isFull() as they work with size - 1, while they should use size instead. Here is the correction to both functions:

        public int size() {
            return size;
        }
        
        public boolean isFull() {
            return size == capacity;
        }
    

    Implement Heapsort

    To implement heapsort, you could add this method:

        public void sort() {
            // HeapSort algorithm:
            int origSize = size;
            while (size > 1) {
                delTop();
            }
            size = origSize;
            // Reverse, so it also is in line with heap property
            for (int i = 0, j = size - 1; i < j; i++, j--) {
                swap(i, j);
            }            
        }
    

    Call this sort method in your main function:

        public static void main(String[] args) {
            MyHeap minHeap = new MyHeap(10, true);
            minHeap.insert(70);
            minHeap.insert(20);
            minHeap.insert(90);
            minHeap.insert(60);
            minHeap.insert(40);
            minHeap.insert(10);
            minHeap.insert(50);
            minHeap.insert(30);
            minHeap.insert(80);
            minHeap.insert(100);
            minHeap.sort();
            System.out.println("Min Heap: " + minHeap);
        
            MyHeap maxHeap = new MyHeap(10, false);
            maxHeap.insert(70);
            maxHeap.insert(20);
            maxHeap.insert(90);
            maxHeap.insert(60);
            maxHeap.insert(40);
            maxHeap.insert(10);
            maxHeap.insert(50);
            maxHeap.insert(30);
            maxHeap.insert(80);
            maxHeap.insert(100);
            minHeap.sort();
            System.out.println("Max Heap: " + maxHeap);
        }
    

    Now it will print out the values in their sorted order.