Play Data Structure (13) -- Priority Queue and Heap Add/Remove Operations

Priority queues and heaps

Priority Queue

Ordinary queue: first in, first out, last in, then out

Priority queue: The queue order has nothing to do with the queue order; it has something to do with priority; (e.g. hospital queue and operating system dynamically select the highest priority task execution)

Graphics: keyword "dynamic"; the elements in the queue are constantly changing, and new elements are constantly entering the queue, not only in order of priority; [Priority can be specifically defined]

Distinction from ordinary queue: Outgoing element is the element with the highest priority, and the head element is also the element with the highest priority, not the element that first enters the queue.

II. Reactor Infrastructure

1. Binary Heap

The conditions for binary heap: binary heap is a complete binary tree; complete binary tree: arrange the elements in order to form a tree; [the bottom layer is all leaf nodes, but there may be leaf nodes above the last layer, but these leaf nodes must be all on the right side of the tree]

The nature of binary heap: [There is no necessary connection between node size and its hierarchy]

1. Maximum heap: The value of a node in the heap is always less than that of its parent; [The value of the father node is greater than that of the left and right children]

2. Minimum heap: The value of a node in the heap is always greater than that of its parent; [The value of the father node is less than that of the left and right children]

Complete binary trees can be implemented with arrays:

Code implementation: MaxHeap.java

public class MaxHeap<E extends Comparable<E>> {	//Comparable

    private Array<E> data;

    public MaxHeap(int capacity){
        data = new Array<>(capacity);
    }

    public MaxHeap(){
        data = new Array<>();
    }

    // Returns the number of elements in the heap
    public int size(){
        return data.getSize();
    }

    // Returns a Boolean value indicating whether the heap is empty
    public boolean isEmpty(){
        return data.isEmpty();
    }

    // Returns the index of the parent node of the element represented by an index in the array representation of a complete binary tree
    private int parent(int index){
        if(index == 0)	//Root node, no father node
            throw new IllegalArgumentException("index-0 doesn't have parent.");
        return (index - 1) / 2;
    }

    // Returns the index of the left child node of the element represented by an index in the array representation of a complete binary tree
    private int leftChild(int index){
        return index * 2 + 1;
    }

    // Returns the index of the right child node of the element represented by an index in the array representation of a complete binary tree
    private int rightChild(int index){
        return index * 2 + 2;
    }
}

3. Adding elements to the heap

Sift up in heap

1. Add element 52 to the heap

2. After adding 52, 52 > 16, the father node is not satisfied with the condition, and the child node is not satisfied with the condition. It should be adjusted to compare with the father node all the way from 52 and exchange the order.

Exchange 52 with 16 in order; but 52 > 41, continue exchanging positions

At this time, 62 > 52, which conforms to the nature of the heap.

Sift up in the heap: 52 floats gradually from the bottom until it is in its proper position to maintain the nature of the heap.

Code implementation:

MaxHeap.java

public class MaxHeap<E extends Comparable<E>> {

    private Array<E> data;

    public MaxHeap(int capacity){
        data = new Array<>(capacity);
    }

    public MaxHeap(){
        data = new Array<>();
    }

    // Returns the number of elements in the heap
    public int size(){
        return data.getSize();
    }

    // Returns a Boolean value indicating whether the heap is empty
    public boolean isEmpty(){
        return data.isEmpty();
    }

    // Returns the index of the parent node of the element represented by an index in the array representation of a complete binary tree
    private int parent(int index){
        if(index == 0)
            throw new IllegalArgumentException("index-0 doesn't have parent.");
        return (index - 1) / 2;
    }

    // Returns the index of the left child node of the element represented by an index in the array representation of a complete binary tree
    private int leftChild(int index){
        return index * 2 + 1;
    }

    // Returns the index of the right child node of the element represented by an index in the array representation of a complete binary tree
    private int rightChild(int index){
        return index * 2 + 2;
    }

    // Adding elements to the heap (new code)
    public void add(E e){
        data.addLast(e);
        siftUp(data.getSize() - 1);
    }

    private void siftUp(int k){

        while(k > 0 && data.get(parent(k)).compareTo(data.get(k)) < 0 ){ 
		//K's father element is compared with K's. If the father element is smaller, the two exchange positions.
            data.swap(k, parent(k));	//swap method for k and k father element exchange
            k = parent(k);
        }
    }
}

Array.java

public class Array<E> {

    private E[] data;
    private int size;

    // Constructor, capacity capacity of the incoming array constructs Array
    public Array(int capacity){
        data = (E[])new Object[capacity];
        size = 0;
    }

    // No parameter constructor, default array capacity=10
    public Array(){
        this(10);
    }

    // Capture the capacity of an array
    public int getCapacity(){
        return data.length;
    }

    // Gets the number of elements in an array
    public int getSize(){
        return size;
    }

    // Whether the return array is empty
    public boolean isEmpty(){
        return size == 0;
    }

    // Insert a new element e at the index location
    public void add(int index, E e){

        if(index < 0 || index > size)
            throw new IllegalArgumentException("Add failed. Require index >= 0 and index <= size.");

        if(size == data.length)
            resize(2 * data.length);

        for(int i = size - 1; i >= index ; i --)
            data[i + 1] = data[i];

        data[index] = e;

        size ++;
    }

    // Add a new element to all elements
    public void addLast(E e){
        add(size, e);
    }

    // Add a new element before all elements
    public void addFirst(E e){
        add(0, e);
    }

    // Get the element of index index index location
    public E get(int index){
        if(index < 0 || index >= size)
            throw new IllegalArgumentException("Get failed. Index is illegal.");
        return data[index];
    }

    // Modify the index index location to e
    public void set(int index, E e){
        if(index < 0 || index >= size)
            throw new IllegalArgumentException("Set failed. Index is illegal.");
        data[index] = e;
    }

    // Find if there is an element e in the array
    public boolean contains(E e){
        for(int i = 0 ; i < size ; i ++){
            if(data[i].equals(e))
                return true;
        }
        return false;
    }

    // Find the index of the element e in the array, and return - 1 if there is no element e
    public int find(E e){
        for(int i = 0 ; i < size ; i ++){
            if(data[i].equals(e))
                return i;
        }
        return -1;
    }

    // Remove the element in the index position from the array and return the deleted element
    public E remove(int index){
        if(index < 0 || index >= size)
            throw new IllegalArgumentException("Remove failed. Index is illegal.");

        E ret = data[index];
        for(int i = index + 1 ; i < size ; i ++)
            data[i - 1] = data[i];
        size --;
        data[size] = null; // loitering objects != memory leak

        if(size == data.length / 4 && data.length / 2 != 0)
            resize(data.length / 2);
        return ret;
    }

    // Delete the first element from the array and return the deleted element
    public E removeFirst(){
        return remove(0);
    }

    // Delete the last element from the array and return the deleted element
    public E removeLast(){
        return remove(size - 1);
    }

    // Delete element e from the array
    public void removeElement(E e){
        int index = find(e);
        if(index != -1)
            remove(index);
    }

    public void swap(int i, int j){

        if(i < 0 || i >= size || j < 0 || j >= size)
            throw new IllegalArgumentException("Index is illegal.");

        E t = data[i];
        data[i] = data[j];
        data[j] = t;
    }

    @Override
    public String toString(){

        StringBuilder res = new StringBuilder();
        res.append(String.format("Array: size = %d , capacity = %d\n", size, data.length));
        res.append('[');
        for(int i = 0 ; i < size ; i ++){
            res.append(data[i]);
            if(i != size - 1)
                res.append(", ");
        }
        res.append(']');
        return res.toString();
    }

    // Change the capacity of array space to the size of new Capacity
    private void resize(int newCapacity){

        E[] newData = (E[])new Object[newCapacity];
        for(int i = 0 ; i < size ; i ++)
            newData[i] = data[i];
        data = newData;
    }
}

4. Extracting elements from the heap

Extract the element, only take out the heap top element (the root node of the binary tree), which is the largest element stored in the binary tree.

Procedure: 1. Access the root node, the elements with index 0 in the array; take it away, for the whole heap, it can be seen as having two subtrees;

2. Top the last element (16) of the heap to the top of the heap; change the array 0 to 16 and delete the corresponding 16 of the array 10

3. But if 16 is on the top of the heap, it breaks the nature of the heap, 16 < 52 < 30, which does not conform to the nature that the father node in the heap is larger than the left and right children.

Sift Down: Elements sink in the heap;

The sinking process 1. The father node was compared with the left and right children (16 compared with 52 and 30);

2. Choose the bigger elements in children and exchange positions.

3. The father node continues to compare with the left and right children, repeating the steps above.

4.16 > 15, satisfying the nature of the heap, the final shape is shown below.

Code implementation: MaxHeap.java

public class MaxHeap<E extends Comparable<E>> {

    private Array<E> data;

    public MaxHeap(int capacity){
        data = new Array<>(capacity);
    }

    public MaxHeap(){
        data = new Array<>();
    }

    // Returns the number of elements in the heap
    public int size(){
        return data.getSize();
    }

    // Returns a Boolean value indicating whether the heap is empty
    public boolean isEmpty(){
        return data.isEmpty();
    }

    // Returns the index of the parent node of the element represented by an index in the array representation of a complete binary tree
    private int parent(int index){
        if(index == 0)
            throw new IllegalArgumentException("index-0 doesn't have parent.");
        return (index - 1) / 2;
    }

    // Returns the index of the left child node of the element represented by an index in the array representation of a complete binary tree
    private int leftChild(int index){
        return index * 2 + 1;
    }

    // Returns the index of the right child node of the element represented by an index in the array representation of a complete binary tree
    private int rightChild(int index){
        return index * 2 + 2;
    }

    // Adding elements to the heap
    public void add(E e){
        data.addLast(e);
        siftUp(data.getSize() - 1);
    }

    private void siftUp(int k){

        while(k > 0 && data.get(parent(k)).compareTo(data.get(k)) < 0 ){
            data.swap(k, parent(k));
            k = parent(k);
        }
    }

    // The largest element in the heap
    public E findMax(){
        if(data.getSize() == 0)
            throw new IllegalArgumentException("Can not findMax when heap is empty.");
        return data.get(0);
    }

    // Remove the largest element in the heap (new code)
    public E extractMax(){

        E ret = findMax();

        data.swap(0, data.getSize() - 1);
        data.removeLast();	//Delete the largest element
        siftDown(0);	//Subsidence operation, corresponding index is 0

        return ret;
    }

    private void siftDown(int k){

        while(leftChild(k) < data.getSize()){//The left child's index is smaller than the total number of elements in the array, and the k node has no children.
            int j = leftChild(k); // In this cycle, data[k] and data[j] exchange locations
            if( j + 1 < data.getSize() &&
                    data.get(j + 1).compareTo(data.get(j)) > 0 )
                j ++;	//j Storage is actually the index of the right child
            // data[j] is the maximum in leftChild and rightChild

            if(data.get(k).compareTo(data.get(j)) >= 0 ) //Comparing k with j, the biggest element in children
                break;

            data.swap(k, j);	//If J > k, the positions of the two are exchanged
            k = j;
        }
    }
}

Main. Java (test)

import java.util.Random;

public class Main {

    public static void main(String[] args) {

        int n = 1000000;	//Number of random numbers

        MaxHeap<Integer> maxHeap = new MaxHeap<>();
        Random random = new Random();
        for(int i = 0 ; i < n ; i ++)
            maxHeap.add(random.nextInt(Integer.MAX_VALUE));//Sort 1000000 random numbers (from large to small)

        int[] arr = new int[n];
        for(int i = 0 ; i < n ; i ++)
            arr[i] = maxHeap.extractMax();

        for(int i = 1 ; i < n ; i ++)
            if(arr[i-1] < arr[i])	//Compare adjacent numbers (right > left)
                throw new IllegalArgumentException("Error");

        System.out.println("Test MaxHeap completed.");
    }
}

Output:

Time complexity analysis: The time complexity of add and extract Max is O(log n), [or the level of binary tree height, but for heap, because it is a complete binary tree, it will never become linked list form, there is no worst case of n].

 

Keywords: Java less

Added by amazinggrace1983 on Thu, 25 Jul 2019 12:25:19 +0300