Blog

How to Efficiently Implement Priority Queues in Java: A Guide to Using Binary Heaps

Creating a priority queue in Java can be efficiently achieved using various data structures such as binary heaps, Fibonacci heaps, and binomial heaps. Each of these structures offers different advantages in terms of insertion, deletion, and decrease-key operations, which are crucial for priority queue operations.

Below, we’ll outline implementations for each and discuss why Fibonacci Heap is often considered superior to Binary Heap for certain applications.

1. Binary Heap

A binary heap is a complete binary tree where each node is smaller than its children for a min-heap or larger for a max-heap. It’s efficient for both insertion and removal operations.

Binary heaps are particularly suited for priority queue implementations because they can ensure that the queue’s “peek” operation (finding the minimum or maximum) is always O(1), while insertion and removal operations are logarithmic (O(log n)) in complexity.

Implementing a Priority Queue with Binary Heap

Here’s a simple Java class that implements a min-priority queue using a binary heap:

import java.util.*;
public class BinaryHeapPriorityQueue{
    private List<Integer> heap;
    public BinaryHeapPriorityQueue(){
        heap = new ArrayList<>();
    }
    public void insert(int element){
        heap.add(element);
        int currentIndex = heap.size() - 1;
        while(currentIndex > 0){
            int parentIndex = (currentIndex -1) /2;
            if(heap.get(currentIndex) < heap.get(parentIndex)){
                Collections.swap(heap, currentIndex, parentIndex);
                currentIndex = parentIndex;
            }else{
                break;
            }
        }
    }

    public int remove(){
        if(heap.size() == 0)
            throw new NoSuchElementException("Heap is empty");
        int removeItem = heap.get(0);
        heap.set(0, heap.get(heap.size()-1));
        heap.remove(heap.get(heap.size() -1));
        heapify(0);
        return removeItem;
    }

    private void heapify(int index){
        int leftChildIndex = 2 *index +1;
        int rightChildIndex = 2 * index +2;
        int smallestIndex = index;
        if(leftChildIndex < heap.size() && heap.get(leftChildIndex) < heap.get(smallestIndex)){
            smallestIndex = leftChildIndex;

        }

        if(rightChildIndex < heap.size() && heap.get(rightChildIndex) < heap.get(smallestIndex)){
            smallestIndex = rightChildIndex;
        }
        if(index != smallestIndex){
            Collections.swap(heap, index, smallestIndex);
            heapify(smallestIndex);
        }
    }

    public static void main(String[] args) {
        BinaryHeapPriorityQueue  binaryHeapPriorityQueue = new BinaryHeapPriorityQueue();
        binaryHeapPriorityQueue.insert(-12);
        binaryHeapPriorityQueue.insert(12);
        binaryHeapPriorityQueue.insert(13);
        binaryHeapPriorityQueue.insert(1);
        binaryHeapPriorityQueue.insert(2);
        System.out.println(binaryHeapPriorityQueue.remove());
        System.out.println(binaryHeapPriorityQueue.remove());

    }
}



                                          

2. Fibonacci Heap

The Fibonacci Heap is a more complex structure. It offers better average-case performance for some operations, particularly for decrease-key and merge operations, making it ideal for algorithms like Dijkstra’s shortest path.

Implementing a full Fibonacci Heap in Java is quite involved due to its complexity, especially in handling all operations efficiently. Here’s a simplified version to illustrate its structure:

public class FibonacciHeapNode {
    int key;
    FibonacciHeapNode child;
    FibonacciHeapNode left;
    FibonacciHeapNode right;
    int degree;
    boolean mark;

    public FibonacciHeapNode(int key) {
        this.key = key;
        this.degree = 0;
        this.mark = false;
        this.left = this;
        this.right = this;
    }
}

public class FibonacciHeap {
    private FibonacciHeapNode min;
    private int n;

    public FibonacciHeap() {
        min = null;
        n = 0;
    }

    public void insert(int key) {
        FibonacciHeapNode node = new FibonacciHeapNode(key);
        if (min != null) {
            node.left = min;
            node.right = min.right;
            min.right = node;
            node.right.left = node;
            if (key < min.key) {
                min = node;
            }
        } else {
            min = node;
        }
        n++;
    }

    // Additional methods for decreaseKey, delete, etc., are required for a complete implementation
}
Avatar

Neelabh

About Author

As Neelabh Singh, I am a Senior Software Engineer with 6.6 years of experience, specializing in Java technologies, Microservices, AWS, Algorithms, and Data Structures. I am also a technology blogger and an active participant in several online coding communities.

You may also like

Blog Design Pattern

Understanding the Builder Design Pattern in Java | Creational Design Patterns | CodeTechSummit

Overview The Builder design pattern is a creational pattern used to construct a complex object step by step. It separates
Blog Tech Toolkit

Base64 Decode

Base64 encoding is a technique used to encode binary data into ASCII characters, making it easier to transmit data over