Hello! Welcome to our next lesson on heap-based algorithms.
In our last session, we mastered an efficient, linear-time algorithm to build a heap from an unordered array using the buildHeap method. This powerful technique is the first of two key ingredients for one of the most important comparison-based sorting algorithms.
Today, you will learn to implement heap sort. We will combine the buildHeap operation with the heap's property of providing quick access to the maximum element to create an efficient, in-place sorting algorithm. Heap sort is a classic algorithm, valued for its guaranteed performance and constant space usage, making it a frequent topic in technical interviews.
1. The Two Phases of Heap Sort
Heap sort works in two distinct phases, both performed on the same array in-place:
-
Heapify Phase: Transform the entire unsorted input array into a max-heap. We will use the efficient
buildHeapalgorithm we learned in the previous lesson. After this phase, the largest element in the array is guaranteed to be at the root (index 0). -
Sort-down Phase: Repeatedly extract the largest element from the heap and move it to its final sorted position at the end of the array.
- Swap the root element (the current maximum) with the last element of the heap.
- The largest element is now at the end of the array, in its correct sorted position.
- Decrease the considered size of the heap by one, effectively "locking in" the sorted element at the end.
- The new root element may violate the max-heap property. Restore the heap by calling
heapify(orsiftDown) on the root of the now smaller heap. - Repeat this process until the heap is empty.
Let's watch a video that visualizes this entire process.
Heap Sort Visually Explained | Sorting Algorithm | Heaps
The video 'Heap Sort Visually Explained' by ByteQuest provides a clear, modern animation of the heap sort algorithm. It correctly starts with the efficient build-heap phase and then moves to the sorting phase.
Watch from 00:24 to 02:31. The video first shows the build max heap step (which you learned last time) and then demonstrates the core sorting loop: swapping the root with the last element and calling heapify down to restore the heap property.
The key insight is that by swapping the max element with the last element of the heap, we are simultaneously moving the largest unsorted item to its final place while also using that now-vacant spot to continue the heap operations. This is what makes the algorithm in-place.
2. A Detailed Walkthrough
Let's dive deeper into the mechanics of the "sort-down" phase. The Happy Coders article provides an excellent set of diagrams that walk through this process step-by-step.
Heapsort – Algorithm, Source Code, Time Complexity
The article 'Heapsort – Algorithm, Source Code, Time Complexity' provides a rich, visual explanation that will solidify your understanding.
Focus on 'Phase 2: Sorting the Array'. Start reading from that heading and follow the diagrams. Observe how after each swap, the largest element is moved to the end, and the heap is 'repaired' using the heapify method on the root.
As you can see from the walkthrough, the process systematically places the largest elements at the end of the array, one by one, shrinking the heap from the right and growing the sorted portion from the right.

3. Java Implementation
Now, let's translate this logic into Java code. Given your background, you'll appreciate how the algorithm's logic maps directly to a clean implementation. The main heapSort method orchestrates the two phases, relying on a helper heapify function (the siftDown you know well) to maintain the heap property.
We will again refer to the Happy Coders article, as it provides a complete and well-structured Java implementation.
Heapsort – Algorithm, Source Code, Time Complexity
This section of the article provides the complete Java code for Heap Sort.
Read the 'Heapsort Java Code Example' section carefully. Examine the sort(), buildHeap(), and heapify() methods. Notice that the heapify method is implemented iteratively using a while loop, which is slightly more space-efficient than a recursive approach as it avoids extra call stack frames.
Let's break down the essential pieces of code you just reviewed.
The heapSort method:
This is the main driver. It first calls buildHeap and then enters the sorting loop.
public void heapSort(int[] arr) {
int n = arr.length;
// Phase 1: Build a max-heap from the array.
// We start from the last non-leaf node and go up to the root.
for (int i = n / 2 - 1; i >= 0; i--) {
heapify(arr, n, i);
}
// Phase 2: One by one extract an element from heap
for (int i = n - 1; i > 0; i--) {
// Move current root (max element) to the end
int temp = arr[0];
arr[0] = arr[i];
arr[i] = temp;
// call heapify on the reduced heap of size 'i'
heapify(arr, i, 0);
}
}
The heapify (sift-down) method:
This is the workhorse function that maintains the max-heap property.
// To heapify a subtree rooted with node i which is an index in arr[].
// n is the size of the heap.
void heapify(int[] arr, int n, int i) {
int largest = i; // Initialize largest as root
int left = 2 * i + 1; // left child
int right = 2 * i + 2; // right child
// If left child is larger than root
if (left < n && arr[left] > arr[largest]) {
largest = left;
}
// If right child is larger than largest so far
if (right < n && arr[right] > arr[largest]) {
largest = right;
}
// If largest is not root
if (largest != i) {
// Swap arr[i] and arr[largest]
int swap = arr[i];
arr[i] = arr[largest];
arr[largest] = swap;
// Recursively heapify the affected sub-tree
heapify(arr, n, largest);
}
}
Note: This version is recursive for clarity. The iterative version from the article is equally valid and slightly more optimized for space.
Test your understanding!
Given the array [3, 7, 1, 8, 2], what is the state of the array after the first two iterations of the sorting loop (the second phase of heap sort)?
Hint: First, you must buildHeap on the array. Then, perform the swap-and-heapify step twice.
Show answer
-
Build Heap Phase:
- Initial array:
[3, 7, 1, 8, 2] - Start
heapifyati = 5/2 - 1 = 1.heapify(arr, 5, 1)swaps7and8. Array becomes[3, 8, 1, 7, 2]. - Next
i = 0.heapify(arr, 5, 0)on3.3's children are8and1. Swap3and8. Array becomes[8, 3, 1, 7, 2].3is now at index 1. Its children are7and2.7is larger, so swap3and7. Array becomes[8, 7, 1, 3, 2]. - After
buildHeap: The max-heap is[8, 7, 1, 3, 2].
- Initial array:
-
Sort-down Phase (Iteration 1):
- Swap root
8with last element2. Array:[2, 7, 1, 3, 8]. - Heap size is now 4. Call
heapify(arr, 4, 0)on the new root2. 2's children are7and1. Swap2and7. Array:[7, 2, 1, 3, 8].2is now at index 1. Its only child in the heap of size 4 is3. Swap2and3. Array:[7, 3, 1, 2, 8].- After 1st iteration:
[7, 3, 1, 2, 8].
- Swap root
-
Sort-down Phase (Iteration 2):
- Swap root
7with last element of the heap2. Array:[2, 3, 1, 7, 8]. - Heap size is now 3. Call
heapify(arr, 3, 0)on the new root2. 2's children are3and1. Swap2and3. Array:[3, 2, 1, 7, 8].- After 2nd iteration:
[3, 2, 1, 7, 8].
- Swap root
4. Algorithm Analysis and Properties
For technical interviews, simply knowing how to implement an algorithm is not enough. You must also understand its performance characteristics and trade-offs.
Time Complexity
- Heapify Phase (
buildHeap): As we learned, this takes time. - Sort-down Phase: This loop runs times. In each iteration, we do one swap () and one call to
heapifyon the root. Theheapifyoperation's complexity is proportional to the height of the heap, which is where is the current heap size. Since goes from down to 1, the total time for this phase is . - Total Time Complexity: . This is the time complexity for the worst, average, and best cases.
Space Complexity
Heap sort is an in-place algorithm. The sorting is done directly within the input array. If using an iterative heapify, you only need a few extra variables for loops and swaps, making the auxiliary space complexity . If using a recursive heapify, the space complexity becomes due to the recursion call stack. For interviews, it's best to highlight the potential with an iterative implementation.
Stability
Heap sort is not stable. This means that if there are two equal elements in the input array, their relative order is not guaranteed to be preserved in the sorted output. Long-range swaps during the heapify process can easily change their original order.
This quick video and detailed article section will help you consolidate these facts.
Heap Sort Visually Explained | Sorting Algorithm | Heaps
Let's revisit the ByteQuest video for a concise summary of the complexity.
Watch from 02:31 to 03:23. It clearly breaks down the time and space complexity, confirming our analysis.
Heapsort – Algorithm, Source Code, Time Complexity
For a deeper dive into why heap sort is not stable, this section of the Happy Coders article is perfect.
Read the section 'Further Characteristics of Heapsort', focusing on 'Space Complexity of Heapsort' and 'Stability of Heapsort'. The stability section provides a clear counterexample.
Conclusion
Congratulations! You've successfully implemented and analyzed Heap Sort. This is a significant milestone in your study of sorting algorithms. Its combination of time and space complexity makes it a unique and powerful tool.
Key Takeaways:
- Heap Sort is an in-place sorting algorithm with a time complexity of .
- It works in two phases: first building a max-heap () and then repeatedly swapping the root with the last element and heapifying the reduced heap ().
- Its auxiliary space complexity is (for an iterative implementation), which is its main advantage over Merge Sort.
- Unlike Merge Sort, Heap Sort is not stable.
Preview of the Next Lesson:
The heap data structure isn't just for sorting. Its primary application is often as the backing structure for a Priority Queue. In our next lesson, we will implement a priority queue using the heap operations you now know so well and use it to solve common interview problems like finding the "Top K" elements in a stream of data.