Java data structure | Analog implementation of priority queue

Table of contents

I. Introduction

2. Heap simulation implements priority queue

2.1 The concept of heap

2.2 Properties of the heap

2.3 Heap storage method

2.4 Heap creation


I. Introduction

We have learned about queues before. Queues are a first-in-first-out (FIFO) data structure, but in some cases, the data to be manipulated may have priority. Generally, when dequeuing, elements with higher priority may be required to be out of the queue first. , In this scenario, it is obviously inappropriate to use the queue. For example: when playing a game on a mobile phone, if there is an incoming call, the system should prioritize the incoming call.

In this case, our data structure should provide two basic operations, one is to return the highest priority object, and the other is to add new objects. This data structure is the Priority Queue (Priority Queue).

2. Heap simulation implements priority queue

The bottom layer of PriorityQueue in JDK1.8 uses the heap data structure, and the heap actually adjusts some elements on the basis of a complete binary tree.

2.1 The concept of heap

If there is a set of key codes K = {k0, k1, k2, ..., kn-1}, store all its elements in a one-dimensional array in the order of a complete binary tree, and satisfy: Ki <= K2i +1 and Ki<= K2i+2 (Ki >= K2i+1 and Ki >= K2i+2) i = 0, 1, 2..., then it is called a small heap (or a large heap). The heap with the largest root node is called the largest heap or large root heap, and the heap with the smallest root node is called the smallest heap or small root heap.

2.2 Properties of the heap

  • The value of a node in the heap is always not greater than or not less than the value of its parent node;

  • The heap is always a complete binary tree.

Unlike the binary search tree, the left and right nodes of the heap are smaller than the root node, and the values ​​​​of the left and right nodes have no size relationship

2.3 Heap storage method

The heap is a complete binary tree, so the rules of layer order can be used to store sequentially

Note: For non-complete binary trees, it is not suitable to use sequential storage , because empty nodes must be stored in order to be able to restore the binary tree space, which will lead to relatively low space utilization

After storing the elements in the array, the tree can be restored according to property 5 of the chapter on binary trees. Assuming that i is the subscript of the node in the array, then:

  • If i is 0, the node represented by i is the root node, otherwise the parent node of i node is (i - 1)/2

  • If 2 * i + 1 is less than the number of nodes, the subscript of the left child of node i is 2 * i + 1, otherwise there is no left child

  • If 2 * i + 2 is less than the number of nodes, the subscript of the right child of node i is 2 * i + 2, otherwise there is no right child

2.4 Heap creation

  • heap adjustment

Condition: the left and right subtrees must already be heaps to adjust

For the data in the collection {27,15,19,18,28,34,65,49,25,37}, what if it is created into a heap?

Observing this tree, it can be found that the left and right sides of the root node have satisfied the properties of the small root heap, and only need to adjust the root node downward

Adjustment process:

  • Set the root node of this binary tree as the parent node,

  • Compare the value of the child nodes of the parent node, and set the smaller node among the child nodes as the child node

    • initial state

  • Compare the size of the value of the parent node and the child node

    • If parent > child, the nature of the small root heap is not satisfied, and the two are exchanged.

    • If parent < child, the property of the small root heap is satisfied, no exchange is performed, and the adjustment ends.

  • After each exchange, update the position of child and parent, parent =child, child = 2 *parent+1;

  • Code implementation: Time complexity: O(logN) — parent is fixed, child x 2 each time

//    小根堆的向下调整(满足parent的左子树和右子树已经是堆了)
    public void shiftDown(int parent,int len){
        int child = 2*parent +1;
//        必须保证右左孩子
        while(child < len){
//            找到左右孩子的最小值
            if(child +1 < len && elem[child] > elem[child+1]){
                child++;
            }
            if(elem[child] < elem[parent]){
                int tmp = elem[child];
                elem[child] = elem[parent];
                elem[parent] = tmp;
//                向下调整重新更新
                parent =child;
                child = 2 *parent+1;
            }else{
                break;
            }
        }
    }

For the idea of ​​downward adjustment, we can build a heap, build an array into a heap, start from the penultimate non-leaf node, traverse the array from the back to the front, and adjust downwards in turn to get a small root heap

Example: Build the following array [9,5,2,7,3,6,8] into a small root heap

At this time, the trees on both sides of the left and right children of the root node meet the characteristics of the small root heap. You only need to adjust the tree rooted at 9 to adjust downward. The adjustment process and results are as follows

The final result is

  • Code

Time complexity: The time complexity of building a heap is O(n) (complex mathematical calculations)

    public void crearHeap(){
//        最后一个节点的下标为  i  = usedSize -1
//         (i - 1) / 2 即为最后一个非叶子节点的下标
        for(int parent = (usedSize-1-1)/2; parent >= 0;parent--){
           shiftDown(parent,usedSize);
          //对每一个非叶子节点进行向下调整
        }
    }

usedsize - is the subscript of the last leaf node, ((usedsize -1) - 1) / 2 is the subscript of the last non-leaf node

  • heap adjustment

When we insert elements, we still need to ensure that the heap is a large root heap, so we need to adjust the heap upwards

Steps to adjust the heap upwards

  • Set the inserted element, that is, the last leaf node, to child, and its parent node to parent = (child-1) /2

  • When child > parent, the nature of the large root heap is not satisfied, and the value of the parent node is exchanged with the value of the leaf node

  • When child <parent, the condition is met and no adjustment is required

After the adjustment is completed, re-update the positions of parent and child, that is, child = parent, parent = 2 * child +1

The code to adjust upwards to the big root heap is as follows:

  public void shiftUp(int child){
        int parent = (child-1) /2;
        while(child > 0){
            if(elem[child] > elem[parent]){
                int tmp = elem[child];
                elem[child] = elem[parent];
                elem[parent] = tmp;
                child = parent;
                parent = (child -1)/2;
            }else {
                break;
            }
        }
    }
  • When an element is inserted into the heap, the code implements

public void offer(int val){
//如果堆为满,则对数组进行扩容
        if(isFull()){
            elem = Arrays.copyOf(elem,2*elem.length);
        }
 //将插入的元素设置为堆的最后一个元素
        this.elem[usedSize] = val;
        usedSize++;
 //将堆中元素进行向上调整
        shiftUp(usedSize-1);
    }
     public boolean isFull(){
        return elem.length == usedSize;
    }
  • Deletion of the heap (delete the top element of the heap)

    • Swaps the top element of the heap with the value of the last node in the heap in the queue

    • Decrements the value of an element in the heap by one

    • Adjusts down the elements in the heap

The code is implemented as follows:

public int pop(){
        if(isEmpty()){
            return -1;
        }
        int tmp = elem[0];
        elem[0] = elem[usedSize -1];
        elem[usedSize -1] = tmp;
        usedSize--;
    //将堆中元素进行向下调整
        shiftDown(0,usedSize);
        return tmp;
    }
  • Implementation of priority queue using heap simulation

public class TestHeap {
    public int[] elem;
    public int usedSize;
    public static int DEFAULT_SIZE = 10 ;
    public TestHeap() {
        this.elem = new int[DEFAULT_SIZE];
    }
    public void init(int[] array){
        for(int i = 0; i < array.length;i++){
            elem[i] = array[i];
            usedSize++;
        }
    }
//   建堆的时间复杂度为O(n)
    public void crearHeap(){
//        最后一个节点的下标为  i  = usedSize -1
//         (i - 1) / 2 即为父亲节点的下标
        for(int parent = (usedSize-1-1)/2; parent >= 0;parent--){
           shiftDown(parent,usedSize);
        }
    }
    /**
     *
     * @param parent 每棵子树的根节点
     * @param len 每棵子树的
     * 时间复杂度:O(log(n))
     */
//    小根堆的向下调整(满足parent的左子树和右子树已经是堆了)
    public void shiftDown(int parent,int len){
        int child = 2*parent +1;
//        必须保证右左孩子
        while(child < len){
//            找到左右孩子的最小值
            if(child +1 < len && elem[child] > elem[child+1]){
                child++;
            }
            if(elem[child] < elem[parent]){
                int tmp = elem[child];
                elem[child] = elem[parent];
                elem[parent] = tmp;
//                向下调整重新更新
                parent =child;
                child = 2 *parent+1;
            }else{
                break;
            }
        }
    }
    public void offer(int val){
        if(isFull()){
            elem = Arrays.copyOf(elem,2*elem.length);
        }
        this.elem[usedSize] = val;
        usedSize++;
        shiftUp(usedSize-1);
    }
//    向上调整
        public void shiftUp(int child){
        int parent = (child-1) /2;
        while(child > 0){
            if(elem[child] > elem[parent]){
                int tmp = elem[child];
                elem[child] = elem[parent];
                elem[parent] = tmp;
                child = parent;
                parent = (child -1)/2;
            }else {
                break;
            }
        }
    }
    public boolean isFull(){
        return elem.length == usedSize;
    }
    public boolean isEmpty(){
        return usedSize == 0;
    }
    public int pop(){
        if(isEmpty()){
            return -1;
        }
        int tmp = elem[0];
        elem[0] = elem[usedSize -1];
        elem[usedSize -1] = tmp;
        usedSize--;
        shiftDown(0,usedSize);
        return tmp;
    }
    public int peek(){
        if(isEmpty()){
            return -1;
        }
        return elem[0];
    }
}

ced485cbb11e458d81a746890b32cf3f.gif

Guess you like

Origin blog.csdn.net/m0_56361048/article/details/127839213