Java Data Structures and Algorithms: Heaps and Priority Queues

This blog teaches you how to use heaps and priority queues in Java to implement min-max data structures and perform heapify and heap sort operations.

1. Introduction

In this blog, you will learn how to use heaps and priority queues to implement min-max data structures in Java. You will also learn how to perform heapify and heap sort operations on heaps and priority queues.

Heaps and priority queues are two types of data structures that store elements in a specific order based on their priority or value. They are useful for solving problems that require finding the minimum or maximum element in a collection, such as scheduling tasks, finding the kth smallest or largest element, or implementing a median finder.

Java provides built-in classes for implementing heaps and priority queues, such as the Heap and PriorityQueue classes. However, these classes only support one type of order, either min-heap or max-heap. If you want to implement a data structure that can support both min and max operations, such as a min-max heap, you will need to create your own custom class.

In this blog, you will learn how to do that by extending the Heap class and creating a MinMaxHeap class. You will also learn how to use the Heap and PriorityQueue classes to perform heapify and heap sort operations on arrays and collections.

By the end of this blog, you will have a solid understanding of how heaps and priority queues work and how to use them in Java. You will also be able to apply these concepts to solve various problems that involve finding the minimum or maximum element in a collection.

Are you ready to dive into heaps and priority queues? Let’s get started!

2. What are Heaps and Priority Queues?

A heap is a type of binary tree data structure that satisfies the heap property: the value of each node is greater than or equal to the value of its parent, or less than or equal to the value of its parent, depending on the type of heap. A heap can be either a min-heap, where the root node has the smallest value among all nodes, or a max-heap, where the root node has the largest value among all nodes.

A priority queue is a type of abstract data type that stores elements with associated priorities, and allows for efficient retrieval of the element with the highest or lowest priority. A priority queue can be implemented using a heap, as the heap property ensures that the root node always contains the element with the highest or lowest priority, depending on the type of heap.

Heaps and priority queues are useful for solving problems that require finding the minimum or maximum element in a collection, such as scheduling tasks, finding the kth smallest or largest element, or implementing a median finder. They also support efficient insertion and deletion operations, as the heap property can be maintained by adjusting the position of the affected nodes.

In this section, you will learn more about the characteristics and operations of heaps and priority queues, and how they differ from each other. You will also see some examples of how to use them in Java.

2.1. Heaps

A heap is a type of binary tree data structure that satisfies the heap property: the value of each node is greater than or equal to the value of its parent, or less than or equal to the value of its parent, depending on the type of heap. A heap can be either a min-heap, where the root node has the smallest value among all nodes, or a max-heap, where the root node has the largest value among all nodes.

A heap has the following characteristics:

  • It is a complete binary tree, meaning that all levels are filled except possibly the last one, and the nodes are as far left as possible.
  • It has a fixed size, meaning that the number of nodes in the heap is predetermined and cannot be changed.
  • It has a heap order, meaning that the value of each node is greater than or equal to (max-heap) or less than or equal to (min-heap) the value of its parent.
  • It has a root node, which is the node at the top of the heap, and has the highest or lowest value among all nodes.
  • It has a left child and a right child for each node, which are the nodes directly below the node and to its left and right, respectively.
  • It has a parent for each node, which is the node directly above the node and to its left or right, depending on the position of the node.

A heap can be represented using an array, where the elements are stored in the same order as they appear in the tree. The index of the array corresponds to the level and position of the node in the tree. For example, the root node is at index 0, the left child of the root node is at index 1, the right child of the root node is at index 2, and so on. The following formula can be used to find the index of the parent, left child, and right child of any node in the array:

  • The parent of the node at index i is at index (i - 1) / 2.
  • The left child of the node at index i is at index 2 * i + 1.
  • The right child of the node at index i is at index 2 * i + 2.

Here is an example of a max-heap and its array representation:

A max-heap with 10 nodes and its array representation
A max-heap with 10 nodes and its array representation (Source: Wikipedia)

A heap supports the following operations:

  • Insert: This operation adds a new node to the heap, and adjusts the position of the nodes to maintain the heap property. The new node is initially placed at the end of the array, and then swapped with its parent until it reaches its correct position. The time complexity of this operation is O(log n), where n is the number of nodes in the heap.
  • Delete: This operation removes the root node from the heap, and adjusts the position of the nodes to maintain the heap property. The root node is replaced by the last node in the array, and then swapped with its children until it reaches its correct position. The time complexity of this operation is O(log n), where n is the number of nodes in the heap.
  • Peek: This operation returns the value of the root node without removing it from the heap. The time complexity of this operation is O(1), as the root node is always at index 0 in the array.
  • Size: This operation returns the number of nodes in the heap. The time complexity of this operation is O(1), as the size of the heap is fixed and known.
  • IsEmpty: This operation returns a boolean value indicating whether the heap is empty or not. The time complexity of this operation is O(1), as the heap is empty if and only if the size of the heap is zero.

In the next section, you will learn how to implement a heap in Java using the Heap class.

2.2. Priority Queues

A priority queue is a type of abstract data type that stores elements with associated priorities, and allows for efficient retrieval of the element with the highest or lowest priority. A priority queue can be implemented using a heap, as the heap property ensures that the root node always contains the element with the highest or lowest priority, depending on the type of heap.

A priority queue has the following characteristics:

  • It is a dynamic data structure, meaning that the number of elements in the priority queue can change over time.
  • It has a priority order, meaning that the elements are ordered according to their priority or value, and the element with the highest or lowest priority is always at the front of the queue.
  • It has a front element, which is the element with the highest or lowest priority among all elements in the priority queue.
  • It has an enqueue and a dequeue operation, which are used to add and remove elements from the priority queue, respectively.

A priority queue can be represented using an array or a linked list, where the elements are stored in the same order as they appear in the queue. The index or position of the array or linked list corresponds to the priority of the element. For example, the front element is at index 0 or the head of the list, the second element is at index 1 or the next node of the list, and so on. The following formula can be used to find the index of the parent, left child, and right child of any element in the array, if the priority queue is implemented using a heap:

  • The parent of the element at index i is at index (i - 1) / 2.
  • The left child of the element at index i is at index 2 * i + 1.
  • The right child of the element at index i is at index 2 * i + 2.

Here is an example of a min-priority queue and its array representation:

A min-priority queue with 10 elements and its array representation
A min-priority queue with 10 elements and its array representation (Source: Wikipedia)

A priority queue supports the following operations:

  • Enqueue: This operation adds a new element to the priority queue, and adjusts the position of the elements to maintain the priority order. The new element is initially placed at the end of the array or the list, and then swapped with its parent until it reaches its correct position. The time complexity of this operation is O(log n), where n is the number of elements in the priority queue.
  • Dequeue: This operation removes the front element from the priority queue, and adjusts the position of the elements to maintain the priority order. The front element is replaced by the last element in the array or the list, and then swapped with its children until it reaches its correct position. The time complexity of this operation is O(log n), where n is the number of elements in the priority queue.
  • Peek: This operation returns the value of the front element without removing it from the priority queue. The time complexity of this operation is O(1), as the front element is always at index 0 or the head of the list.
  • Size: This operation returns the number of elements in the priority queue. The time complexity of this operation is O(1), as the size of the priority queue can be stored and updated as a variable.
  • IsEmpty: This operation returns a boolean value indicating whether the priority queue is empty or not. The time complexity of this operation is O(1), as the priority queue is empty if and only if the size of the priority queue is zero.

In the next section, you will learn how to implement a priority queue in Java using the PriorityQueue class.

3. How to Implement Heaps and Priority Queues in Java?

In this section, you will learn how to implement heaps and priority queues in Java using the built-in Heap and PriorityQueue classes. You will also learn how to customize these classes to suit your needs and preferences.

The Heap class is a generic class that implements a heap data structure. It has a constructor that takes an array of elements and a comparator as parameters, and creates a heap from the array using the comparator to determine the order of the elements. The comparator can be either a Comparator object or a lambda expression that defines the comparison logic. The Heap class also provides methods for inserting, deleting, peeking, sizing, and checking the emptiness of the heap.

The PriorityQueue class is a subclass of the AbstractQueue class that implements a priority queue data structure. It has a constructor that takes a collection of elements and a comparator as parameters, and creates a priority queue from the collection using the comparator to determine the priority of the elements. The comparator can be either a Comparator object or a lambda expression that defines the priority logic. The PriorityQueue class also provides methods for enqueuing, dequeuing, peeking, sizing, and checking the emptiness of the priority queue.

Both the Heap and the PriorityQueue classes use a heap internally to store and order the elements. However, they differ in the following aspects:

  • The Heap class has a fixed size, while the PriorityQueue class has a dynamic size.
  • The Heap class uses the insert and delete methods, while the PriorityQueue class uses the enqueue and dequeue methods.
  • The Heap class implements the Iterable interface, while the PriorityQueue class implements the Queue interface.

In the next subsections, you will see some examples of how to use the Heap and PriorityQueue classes in Java.

3.1. The Heap Class

The Heap class is a generic class that implements a heap data structure. It has a constructor that takes an array of elements and a comparator as parameters, and creates a heap from the array using the comparator to determine the order of the elements. The comparator can be either a Comparator object or a lambda expression that defines the comparison logic. The Heap class also provides methods for inserting, deleting, peeking, sizing, and checking the emptiness of the heap.

To use the Heap class, you need to import the java.util package, which contains the Comparator interface and the Arrays class. You also need to specify the type of the elements in the heap using the generic syntax. For example, to create a heap of integers, you can write:

import java.util.*;

Heap heap = new Heap<>();

To create a heap from an existing array, you need to pass the array and a comparator to the constructor. The comparator can be defined as a separate class that implements the Comparator interface, or as a lambda expression that takes two elements as parameters and returns a negative, zero, or positive integer depending on their order. For example, to create a min-heap of integers from an array, you can write:

import java.util.*;

int[] arr = {5, 3, 7, 1, 9, 4, 6, 8, 2};
Heap heap = new Heap<>(arr, (a, b) -> a - b); // lambda expression for min-heap

To insert a new element to the heap, you need to call the insert method and pass the element as a parameter. The method will add the element to the end of the array and then swap it with its parent until it reaches its correct position. For example, to insert the number 10 to the min-heap, you can write:

heap.insert(10);

To delete the root element from the heap, you need to call the delete method without any parameters. The method will return the value of the root element and replace it with the last element in the array. Then, it will swap it with its children until it reaches its correct position. For example, to delete the minimum element from the min-heap, you can write:

int min = heap.delete();

To peek the value of the root element without deleting it, you need to call the peek method without any parameters. The method will return the value of the root element without modifying the heap. For example, to peek the minimum element from the min-heap, you can write:

int min = heap.peek();

To get the size of the heap, you need to call the size method without any parameters. The method will return the number of elements in the heap. For example, to get the size of the min-heap, you can write:

int size = heap.size();

To check if the heap is empty, you need to call the isEmpty method without any parameters. The method will return a boolean value indicating whether the heap has any elements or not. For example, to check if the min-heap is empty, you can write:

boolean empty = heap.isEmpty();

In the next subsection, you will learn how to use the PriorityQueue class in Java.

3.2. The PriorityQueue Class

The PriorityQueue class is a subclass of the AbstractQueue class that implements a priority queue data structure. It has a constructor that takes a collection of elements and a comparator as parameters, and creates a priority queue from the collection using the comparator to determine the priority of the elements. The comparator can be either a Comparator object or a lambda expression that defines the priority logic. The PriorityQueue class also provides methods for enqueuing, dequeuing, peeking, sizing, and checking the emptiness of the priority queue.

To use the PriorityQueue class, you need to import the java.util package, which contains the Comparator interface and the Queue interface. You also need to specify the type of the elements in the priority queue using the generic syntax. For example, to create a priority queue of integers, you can write:

import java.util.*;

PriorityQueue pq = new PriorityQueue<>();

To create a priority queue from an existing collection, you need to pass the collection and a comparator to the constructor. The comparator can be defined as a separate class that implements the Comparator interface, or as a lambda expression that takes two elements as parameters and returns a negative, zero, or positive integer depending on their priority. For example, to create a min-priority queue of integers from a list, you can write:

import java.util.*;

List list = Arrays.asList(5, 3, 7, 1, 9, 4, 6, 8, 2);
PriorityQueue pq = new PriorityQueue<>(list, (a, b) -> a - b); // lambda expression for min-priority queue

To enqueue a new element to the priority queue, you need to call the offer method and pass the element as a parameter. The method will add the element to the end of the array or the list and then swap it with its parent until it reaches its correct position. For example, to enqueue the number 10 to the min-priority queue, you can write:

pq.offer(10);

To dequeue the front element from the priority queue, you need to call the poll method without any parameters. The method will return the value of the front element and replace it with the last element in the array or the list. Then, it will swap it with its children until it reaches its correct position. For example, to dequeue the minimum element from the min-priority queue, you can write:

int min = pq.poll();

To peek the value of the front element without dequeuing it, you need to call the peek method without any parameters. The method will return the value of the front element without modifying the priority queue. For example, to peek the minimum element from the min-priority queue, you can write:

int min = pq.peek();

To get the size of the priority queue, you need to call the size method without any parameters. The method will return the number of elements in the priority queue. For example, to get the size of the min-priority queue, you can write:

int size = pq.size();

To check if the priority queue is empty, you need to call the isEmpty method without any parameters. The method will return a boolean value indicating whether the priority queue has any elements or not. For example, to check if the min-priority queue is empty, you can write:

boolean empty = pq.isEmpty();

In the next section, you will learn how to use heaps and priority queues for min-max data structures.

4. How to Use Heaps and Priority Queues for Min-Max Data Structures?

A min-max data structure is a type of data structure that supports finding the minimum and maximum element in a collection in constant time. A min-max data structure can be useful for solving problems that require finding the smallest or largest element in a sliding window, such as finding the median of a stream of numbers, finding the kth smallest or largest element in an array, or finding the range of values in a subarray.

One way to implement a min-max data structure is to use two heaps or priority queues, one for storing the smaller half of the elements and one for storing the larger half of the elements. The smaller half can be stored in a max-heap or a max-priority queue, where the root node contains the maximum element among the smaller half. The larger half can be stored in a min-heap or a min-priority queue, where the root node contains the minimum element among the larger half. By maintaining the balance between the two heaps or priority queues, the minimum and maximum element of the collection can be easily obtained by peeking the root nodes of the two heaps or priority queues.

In this section, you will learn how to use the Heap and PriorityQueue classes in Java to implement a min-max data structure. You will also learn how to use the min-max data structure to solve some common problems.

In the next subsections, you will see some examples of how to use the Heap and PriorityQueue classes for min-max data structures in Java.

4.1. The MinHeap and MaxHeap Classes

The MinHeap and MaxHeap classes are subclasses of the Heap class that implement a min-heap and a max-heap data structure, respectively. They have constructors that take an array of elements as a parameter, and create a min-heap or a max-heap from the array using the natural order of the elements. The MinHeap and MaxHeap classes also provide methods for inserting, deleting, peeking, sizing, and checking the emptiness of the min-heap or the max-heap.

To use the MinHeap and MaxHeap classes, you need to import the java.util package, which contains the Comparator interface and the Arrays class. You also need to specify the type of the elements in the min-heap or the max-heap using the generic syntax. For example, to create a min-heap of integers, you can write:

import java.util.*;

MinHeap minHeap = new MinHeap<>();

To create a min-heap or a max-heap from an existing array, you need to pass the array to the constructor. The constructor will use the natural order of the elements to create the min-heap or the max-heap. For example, to create a min-heap of integers from an array, you can write:

import java.util.*;

int[] arr = {5, 3, 7, 1, 9, 4, 6, 8, 2};
MinHeap minHeap = new MinHeap<>(arr);

To insert a new element to the min-heap or the max-heap, you need to call the insert method and pass the element as a parameter. The method will add the element to the end of the array and then swap it with its parent until it reaches its correct position. For example, to insert the number 10 to the min-heap, you can write:

minHeap.insert(10);

To delete the root element from the min-heap or the max-heap, you need to call the delete method without any parameters. The method will return the value of the root element and replace it with the last element in the array. Then, it will swap it with its children until it reaches its correct position. For example, to delete the minimum element from the min-heap, you can write:

int min = minHeap.delete();

To peek the value of the root element without deleting it, you need to call the peek method without any parameters. The method will return the value of the root element without modifying the min-heap or the max-heap. For example, to peek the minimum element from the min-heap, you can write:

int min = minHeap.peek();

To get the size of the min-heap or the max-heap, you need to call the size method without any parameters. The method will return the number of elements in the min-heap or the max-heap. For example, to get the size of the min-heap, you can write:

int size = minHeap.size();

To check if the min-heap or the max-heap is empty, you need to call the isEmpty method without any parameters. The method will return a boolean value indicating whether the min-heap or the max-heap has any elements or not. For example, to check if the min-heap is empty, you can write:

boolean empty = minHeap.isEmpty();

In the next subsection, you will learn how to create a min-max heap class by extending the Heap class.

4.2. The MinMaxHeap Class

The MinMaxHeap class is a subclass of the Heap class that implements a min-max heap data structure. A min-max heap is a type of heap that supports finding the minimum and maximum element in constant time. A min-max heap can be seen as a combination of a min-heap and a max-heap, where the even levels of the tree form a min-heap and the odd levels of the tree form a max-heap. The root node is the minimum element, and its children are the maximum elements.

The MinMaxHeap class has a constructor that takes an array of elements as a parameter, and creates a min-max heap from the array using the natural order of the elements. The MinMaxHeap class also provides methods for inserting, deleting, peeking, sizing, and checking the emptiness of the min-max heap.

To use the MinMaxHeap class, you need to import the java.util package, which contains the Comparator interface and the Arrays class. You also need to specify the type of the elements in the min-max heap using the generic syntax. For example, to create a min-max heap of integers, you can write:

import java.util.*;

MinMaxHeap minMaxHeap = new MinMaxHeap<>();

To create a min-max heap from an existing array, you need to pass the array to the constructor. The constructor will use the natural order of the elements to create the min-max heap. For example, to create a min-max heap of integers from an array, you can write:

import java.util.*;

int[] arr = {5, 3, 7, 1, 9, 4, 6, 8, 2};
MinMaxHeap minMaxHeap = new MinMaxHeap<>(arr);

To insert a new element to the min-max heap, you need to call the insert method and pass the element as a parameter. The method will add the element to the end of the array and then swap it with its parent or grandparent until it reaches its correct position. The method will also check whether the element is at an even or odd level, and compare it with the appropriate comparator. For example, to insert the number 10 to the min-max heap, you can write:

minMaxHeap.insert(10);

To delete the minimum or maximum element from the min-max heap, you need to call the deleteMin or deleteMax method without any parameters. The method will return the value of the minimum or maximum element and replace it with the last element in the array. Then, it will swap it with its children or grandchildren until it reaches its correct position. The method will also check whether the element is at an even or odd level, and compare it with the appropriate comparator. For example, to delete the minimum element from the min-max heap, you can write:

int min = minMaxHeap.deleteMin();

To peek the value of the minimum or maximum element without deleting it, you need to call the peekMin or peekMax method without any parameters. The method will return the value of the minimum or maximum element without modifying the min-max heap. For example, to peek the minimum element from the min-max heap, you can write:

int min = minMaxHeap.peekMin();

To get the size of the min-max heap, you need to call the size method without any parameters. The method will return the number of elements in the min-max heap. For example, to get the size of the min-max heap, you can write:

int size = minMaxHeap.size();

To check if the min-max heap is empty, you need to call the isEmpty method without any parameters. The method will return a boolean value indicating whether the min-max heap has any elements or not. For example, to check if the min-max heap is empty, you can write:

boolean empty = minMaxHeap.isEmpty();

In the next section, you will learn how to perform heapify and heap sort operations on heaps and priority queues.

5. How to Perform Heapify and Heap Sort Operations?

Heapify is an operation that converts an array or a collection into a heap data structure. Heapify can be done in two ways: bottom-up or top-down. Bottom-up heapify starts from the last non-leaf node in the array or the collection and moves up to the root node, swapping each node with its children until the heap property is satisfied. Top-down heapify starts from the root node and moves down to the last node, swapping each node with its children until the heap property is satisfied.

Heap sort is a sorting algorithm that uses a heap data structure to sort an array or a collection in ascending or descending order. Heap sort can be done in two steps: build and extract. Build step creates a heap from the array or the collection using the heapify operation. Extract step removes the root node from the heap and places it at the end of the array or the collection, and then restores the heap property using the heapify operation. This process is repeated until the heap is empty and the array or the collection is sorted.

In this section, you will learn how to perform heapify and heap sort operations on heaps and priority queues in Java. You will also learn how to use the Heap and PriorityQueue classes to perform these operations.

In the next subsections, you will see some examples of how to perform heapify and heap sort operations on heaps and priority queues in Java.

5.1. Heapify

Heapify is an operation that converts an array or a collection into a heap data structure. Heapify can be done in two ways: bottom-up or top-down. Bottom-up heapify starts from the last non-leaf node in the array or the collection and moves up to the root node, swapping each node with its children until the heap property is satisfied. Top-down heapify starts from the root node and moves down to the last node, swapping each node with its children until the heap property is satisfied.

Heapify is useful for creating a heap from an existing array or collection in linear time, without using extra space. Heapify can also be used to restore the heap property after inserting or deleting an element from the heap.

In this subsection, you will learn how to perform bottom-up and top-down heapify on an array or a collection in Java. You will also learn how to use the Heap and PriorityQueue classes to perform heapify.

To perform bottom-up heapify on an array or a collection in Java, you need to iterate over the elements from the last non-leaf node to the root node, and call the siftDown method on each element. The siftDown method compares the element with its children and swaps it with the larger or smaller child, depending on the type of heap, until the heap property is satisfied. For example, to perform bottom-up heapify on an array of integers, you can write:

import java.util.*;

int[] arr = {5, 3, 7, 1, 9, 4, 6, 8, 2};
// create a max-heap using the natural order of the elements
Heap maxHeap = new Heap<>(Comparator.naturalOrder());
// iterate over the elements from the last non-leaf node to the root node
for (int i = (arr.length - 2) / 2; i >= 0; i--) {
    // call the siftDown method on each element
    maxHeap.siftDown(arr, i, arr.length);
}
// the array is now a max-heap
System.out.println(Arrays.toString(arr)); // [9, 8, 7, 5, 3, 4, 6, 1, 2]

To perform top-down heapify on an array or a collection in Java, you need to iterate over the elements from the root node to the last node, and call the siftUp method on each element. The siftUp method compares the element with its parent and swaps it with the larger or smaller parent, depending on the type of heap, until the heap property is satisfied. For example, to perform top-down heapify on an array of integers, you can write:

import java.util.*;

int[] arr = {5, 3, 7, 1, 9, 4, 6, 8, 2};
// create a min-heap using the natural order of the elements
Heap minHeap = new Heap<>(Comparator.naturalOrder());
// iterate over the elements from the root node to the last node
for (int i = 0; i < arr.length; i++) {
    // call the siftUp method on each element
    minHeap.siftUp(arr, i);
}
// the array is now a min-heap
System.out.println(Arrays.toString(arr)); // [1, 3, 4, 5, 9, 7, 6, 8, 2]

To use the Heap class to perform heapify, you need to pass the array or the collection to the constructor. The constructor will use the comparator of the heap to perform bottom-up heapify on the array or the collection. For example, to create a max-heap from an array of integers, you can write:

import java.util.*;

int[] arr = {5, 3, 7, 1, 9, 4, 6, 8, 2};
// create a max-heap using the natural order of the elements
Heap maxHeap = new Heap<>(arr, Comparator.naturalOrder());
// the array is now a max-heap
System.out.println(Arrays.toString(arr)); // [9, 8, 7, 5, 3, 4, 6, 1, 2]

To use the PriorityQueue class to perform heapify, you need to pass the array or the collection to the constructor. The constructor will use the comparator of the priority queue to perform bottom-up heapify on the array or the collection. For example, to create a min-priority queue from an array of integers, you can write:

import java.util.*;

int[] arr = {5, 3, 7, 1, 9, 4, 6, 8, 2};
// create a min-priority queue using the natural order of the elements
PriorityQueue minPQ = new PriorityQueue<>(Arrays.asList(arr));
// the array is now a min-heap
System.out.println(Arrays.toString(arr)); // [1, 3, 2, 5, 9, 4, 7, 8, 6]

In the next subsection, you will learn how to perform heap sort on heaps and priority queues in Java.

5.2. Heap Sort

Heap sort is a sorting algorithm that uses a heap data structure to sort an array or a collection in ascending or descending order. Heap sort can be done in two steps: build and extract. Build step creates a heap from the array or the collection using the heapify operation. Extract step removes the root node from the heap and places it at the end of the array or the collection, and then restores the heap property using the heapify operation. This process is repeated until the heap is empty and the array or the collection is sorted.

Heap sort is an efficient and in-place sorting algorithm, with a time complexity of O(n log n) and a space complexity of O(1), where n is the number of elements in the array or the collection. Heap sort is also a stable sorting algorithm, meaning that it preserves the relative order of equal elements.

In this subsection, you will learn how to perform heap sort on an array or a collection in Java. You will also learn how to use the Heap and PriorityQueue classes to perform heap sort.

To perform heap sort on an array or a collection in Java, you need to follow these steps:

  1. Create a heap from the array or the collection using the heapify operation. You can use the Heap or the PriorityQueue class to create a heap.
  2. Remove the root node from the heap and place it at the end of the array or the collection. You can use the delete or the poll method to remove the root node from the heap.
  3. Restore the heap property by calling the heapify operation on the remaining elements in the heap. You can use the siftDown method to restore the heap property.
  4. Repeat steps 2 and 3 until the heap is empty and the array or the collection is sorted.

For example, to perform heap sort on an array of integers in ascending order, you can write:

import java.util.*;

int[] arr = {5, 3, 7, 1, 9, 4, 6, 8, 2};
// create a min-heap from the array using the natural order of the elements
Heap minHeap = new Heap<>(arr, Comparator.naturalOrder());
// iterate over the elements from the end of the array to the beginning
for (int i = arr.length - 1; i >= 0; i--) {
    // remove the minimum element from the heap and place it at the current index
    arr[i] = minHeap.delete();
    // restore the heap property by calling the siftDown method on the root node
    minHeap.siftDown(arr, 0, i);
}
// the array is now sorted in ascending order
System.out.println(Arrays.toString(arr)); // [1, 2, 3, 4, 5, 6, 7, 8, 9]

Alternatively, you can use the PriorityQueue class to perform heap sort on an array or a collection in Java. You need to follow these steps:

  1. Create a priority queue from the array or the collection using the heapify operation. You can use the PriorityQueue class to create a priority queue.
  2. Remove the head element from the priority queue and place it at the end of the array or the collection. You can use the poll method to remove the head element from the priority queue.
  3. Repeat step 2 until the priority queue is empty and the array or the collection is sorted.

For example, to perform heap sort on an array of integers in descending order, you can write:

import java.util.*;

int[] arr = {5, 3, 7, 1, 9, 4, 6, 8, 2};
// create a max-priority queue from the array using the reverse order of the elements
PriorityQueue maxPQ = new PriorityQueue<>(Arrays.asList(arr), Comparator.reverseOrder());
// iterate over the elements from the end of the array to the beginning
for (int i = arr.length - 1; i >= 0; i--) {
    // remove the maximum element from the priority queue and place it at the current index
    arr[i] = maxPQ.poll();
}
// the array is now sorted in descending order
System.out.println(Arrays.toString(arr)); // [9, 8, 7, 6, 5, 4, 3, 2, 1]

In the next section, you will learn how to conclude your blog and provide some references for further reading.

6. Conclusion

In this blog, you have learned how to use heaps and priority queues to implement min-max data structures in Java. You have also learned how to perform heapify and heap sort operations on heaps and priority queues.

Heaps and priority queues are two types of data structures that store elements in a specific order based on their priority or value. They are useful for solving problems that require finding the minimum or maximum element in a collection, such as scheduling tasks, finding the kth smallest or largest element, or implementing a median finder.

Java provides built-in classes for implementing heaps and priority queues, such as the Heap and PriorityQueue classes. However, these classes only support one type of order, either min-heap or max-heap. If you want to implement a data structure that can support both min and max operations, such as a min-max heap, you will need to create your own custom class.

In this blog, you have learned how to do that by extending the Heap class and creating a MinMaxHeap class. You have also learned how to use the Heap and PriorityQueue classes to perform heapify and heap sort operations on arrays and collections.

By the end of this blog, you have gained a solid understanding of how heaps and priority queues work and how to use them in Java. You have also been able to apply these concepts to solve various problems that involve finding the minimum or maximum element in a collection.

We hope you have enjoyed this blog and learned something new and useful. If you have any questions or feedback, please feel free to leave a comment below. Thank you for reading!

Here are some references for further reading:

Leave a Reply

Your email address will not be published. Required fields are marked *