Implementation of Priority Queue

A priority queue is an abstract data type similar to a regular queue or stack data structure in which each element additionally has a "priority" associated with it. In a priority queue, an element with high priority is served before an element with low priority.

A common misconception is that a Heap is the same as a Priority Queue, which is not true. A priority queue is an abstract data type, while a Heap is a data structure. Therefore, a Heap is not a Priority Queue, but a way to implement a Priority Queue.

There are multiple ways to implement a Priority Queue, such as array and linked list. However, these implementations only guarantee O(1) time complexity for either insertion or deletion, while the other operation will have a time complexity of O(N)O(N). On the other hand, implementing the priority queue with Heap will allow both insertion and deletion to have a time complexity of O(logN).

Priority Queue/Heap 维护了一个每次poll都是最小元素。是用complete binary tree实现。

所谓的"完全二叉树", 要满足:

  • 除了最后一层, 所有层都排满(没有非空节点)

  • 最后一层的所有非空节点都排在左边

一张图可以直观说明, 完全二叉树其实就是长得像这样:

A heap is a binary tree that meets the following criteria:

  • Is a complete binary tree;

  • The value of each node must be no greater than (or no less than) the value of its child nodes.

There are two kinds of heaps: Max Heap and Min Heap.

  • Max Heap: Each node in the Heap has a value no less than its child nodes. Therefore, the top element (root node) has the largest value in the Heap. (value of node >= values of children)

  • Min Heap: Each node in the Heap has a value no larger than its child nodes. Therefore, the top element (root node) has the smallest value in the Heap. (value of node <= values of children)

min heap 操作:

  • peek(): 返回数组的第一个元素的值,root节点总是最大/小值。time complexity: O(1)

  • insert(): 插入一个新的元素,总是放在数组元素的最后一个位置,然后heapifyUp,调整heap满足min heap。time complexity: O(logN)

  • poll(): 拿出数组的第一个元素,heap中剩下的元素要进行调整,保证heap是min heap的结构,这个过程是heapifyDown。time complexity: O(logN)

  • heapifyUp: 数组新增的最后一个元素与其parent进行比较, 如果新增元素的值小于其parent的值,则进行交换,递归此过程。时间复杂度是O(lgn)。

  • heapifyDown: 把最后一个元素放到root的位置,从root位置开始,比较root位置代表的parent与其左右children的大小,parent总是与children中元素较小的进行交换。 重复这个过程,直到大的元素不再有children。时间复杂度是O(lgn)。

  • build树的时候,因为有n个node,每加入一个都要heapify,所以时间复杂度是O(nlgn)

public class MinHeap {
    // Create a complete binary tree using an array
    // Then use the binary tree to construct a Heap
    int[] minHeap;
    
    // the number of elements is needed when instantiating an array
    // heapSize records the size of the array
    int heapSize;
    
    // realSize records the number of elements in the Heap
    int realSize = 0;

    public MinHeap(int heapSize) {
        this.heapSize = heapSize;
        minHeap = new int[heapSize + 1];
        // To better track the indices of the binary tree, we will not use the 0-th element in the array
        // You can fill it with any value
        minHeap[0] = 0;
    }

    // Function to add an element
    public void add(int element) {
        realSize++;
        
        // If the number of elements in the Heap exceeds the preset heapSize
        // print "Added too many elements" and return
        if (realSize > heapSize) {
            System.out.println("Added too many elements!");
            realSize--;
            return;
        }
        
        // Add the element into the array
        minHeap[realSize] = element;
        
        // Index of the newly added element
        int index = realSize;
        
        // Parent node of the newly added element
        // Note if we use an array to represent the complete binary tree and store the root node at index 1
        // - index of the parent node of any node is [index of the node / 2]
        // - index of the left child node is [index of the node * 2]
        // - index of the right child node is [index of the node * 2 + 1]
        int parent = index / 2;
        // If the newly added element is smaller than its parent node,
        // its value will be exchanged with that of the parent node 
        while ( minHeap[index] < minHeap[parent] && index > 1 ) {
            int temp = minHeap[index];
            minHeap[index] = minHeap[parent];
            minHeap[parent] = temp;
            index = parent;
            parent = index / 2;
        }
    }

    // Get the top element of the Heap
    public int peek() {
        return minHeap[1];
    }

    // Delete the top element of the Heap
    public int pop() {
    
        // If the number of elements in the current Heap is 0,
        // print "Don't have any elements" and return a default value
        if (realSize < 1) {
            System.out.println("Don't have any element!");
            return Integer.MAX_VALUE;
        } else {
            // When there are still elements in the Heap
            // realSize >= 1
            int removeElement = minHeap[1];
            
            // Put the last element in the Heap to the top of Heap
            minHeap[1] = minHeap[realSize];
            realSize--;
            int index = 1;
            
            // When the deleted element is not a leaf node
            while (index <= realSize / 2) {
            
                // the left child of the deleted element
                int left = index * 2;
                
                // the right child of the deleted element
                int right = (index * 2) + 1;
                
                // If the deleted element is larger than the left or right child,
                // its value needs to be exchanged with the smaller value of the left and right child
                if (minHeap[index] > minHeap[left] || minHeap[index] > minHeap[right]) {
                    if (minHeap[left] < minHeap[right]) {
                        int temp = minHeap[left];
                        minHeap[left] = minHeap[index];
                        minHeap[index] = temp;
                        index = left;
                    } else {
                        // maxHeap[left] >= maxHeap[right]
                        int temp = minHeap[right];
                        minHeap[right] = minHeap[index];
                        minHeap[index] = temp;
                        index = right;
                    }
                } else {
                    break;
                }
            }
            return removeElement;
        } 
    }

    // return the number of elements in the Heap
    public int size() {
        return realSize;
    }

    public String toString() {
        if (realSize == 0) {
            return "No element!";
        } else {
            StringBuilder sb = new StringBuilder();
            sb.append('[');
            for (int i = 1; i <= realSize; i++) {
                sb.append(minHeap[i]);
                sb.append(',');
            }
            sb.deleteCharAt(sb.length() - 1);
            sb.append(']');
            return sb.toString();
        }
    }

    public static void main(String[] args) {
        // Test case
        MinHeap minHeap = new MinHeap(3);
        minHeap.add(3);
        minHeap.add(1);
        minHeap.add(2);
        // [1,3,2]
        System.out.println(minHeap.toString());
        // 1
        System.out.println(minHeap.peek());
        // 1
        System.out.println(minHeap.pop());
        // [2, 3]
        System.out.println(minHeap.toString());
        minHeap.add(4);
        // Add too many elements
        minHeap.add(5);
        // [2,3,4]
        System.out.println(minHeap.toString());
    }
}

Last updated