优先级队列 (堆)

目录

一,堆的概念

二, 堆的存储结构

三, 堆的实现

3.1 shiftDown()

3.2 shiftUp()

3.3 shiftDown 与 shiftUp 的时间复杂度

四,堆排序


一,堆的概念

堆常用于实现优先队列(Priority Queue)等应用,其中可以快速查找和删除具有最大(或最小)优先级的元素。堆的操作包括插入新元素、移除堆中的顶部元素(即最值),以及对现有堆进行调整以满足堆序属性。常见的堆调整操作是"上浮"(上滤)和"下沉"(下滤)

堆具有以下两个主要特点:

  1.  堆是一个完全二叉树
  2.  堆中的每个节点的值都要大于等于(或小于等于)其子节点的值。

二, 堆的存储结构

从堆的概念可知,堆在逻辑结构上是一棵完全二叉树,因此可以层序的规则采用顺序的方式来高效存储,也就是说,堆是使用顺序表来存储的,画个图理解一下:

 将元素存储到数组中后,可以根据完全二叉树的性质对树进行还原。假设i为节点在数组中的下标,则有:

  • 如果i = 0,则 i 表示的节点为根节点,否则 i 节点的双亲节点为 (i - 1)/2
  • 如果2 * i + 1 小于节点个数,则节点 i 的左孩子下标为2 * i + 1,否则没有左孩子
  • 如果2 * i + 2 小于节点个数,则节点 i 的右孩子下标为2 * i + 2,否则没有右孩子

三, 堆的实现

在此实现的是大根堆:

public class Heap {
    private int[] elem;
    private int usedSize;

    public Heap(int[] arr){
        elem = new int[arr.length];
        createHeap(arr);
    }

    //建堆
    public void createHeap(int[] arr){
        for (int i = 0; i < arr.length; i++) {
            elem[i] = arr[i];
            usedSize++;
        }
        for (int parent = (usedSize-2)/2; parent >= 0; parent--) {
            shiftDown(parent,usedSize);
        }
    }

    //向下调整
    public void shiftDown(int parent, int len){}

    public void swap(int i, int j){
        int tmp = elem[i];
        elem[i] = elem[j];
        elem[j] = tmp;
    }

    //入堆
    public void push(int val){}

    public boolean isFull(){
        return usedSize == elem.length;
    }

    //向上调整
    public void shiftUp(int child){}

    //出堆顶元素
    public int poll(){
        if(isEmpty()){
            System.out.println("堆中没有元素");
            return -1;
        }
        swap(0,usedSize-1);//将头尾交换
        usedSize--;//去掉堆顶元素
        shiftDown(0,usedSize);//重新排序
        return elem[usedSize];//返回堆顶元素
    }

    public boolean isEmpty(){
        return usedSize == 0;
    }

    //得到堆顶元素
    public int peek(){
        if(!isEmpty()){
            return elem[0];
        }
        System.out.println("堆中没有元素");
        return -1;
    }
}

堆中最核心的代码就是 shiftDown() 和 shiftUp() 方法的实现,其他的方法看看就懂了,下面来详细讲一讲这两个方法的实现

3.1 shiftDown()

比如说我们要将{27,15,19,18,28,34,65,49,25,37}这个数组变成一个堆,我们要如何实现,思路:先找到最后一棵子树,将它变成堆后,依次遍历其他的子树,直到最后一棵子树的根节点是整棵树的根节点结束,看下图: 

代码如下:

    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[parent] < elem[child]){
                swap(parent,child);
                parent = child;
                child = 2*parent + 1;
            }else{
                break;
            }
        }
    }

3.2 shiftUp()

该方法是在插入一个元素时,将其以堆的形式插入,本质上shiftDown 与 shiftUp 的思路差不多:

    public void shiftUp(int child){
        int parent = (child-1)/2;
        while(parent >= 0){
            if(elem[child] > elem[parent]){
                swap(child,parent);
                child = parent;
                parent = (child-1)/2;
            }else {
                break;
            }
        }
    }

3.3 shiftDown 与 shiftUp 的时间复杂度

 ​​​​​​​​​​​​​​

四,堆排序

比如我们要进行升序排序,我们先要建立一个大根堆,再将根节点与最后一个节点交换,这个时候最后一个节点一定是的最大的,然后进行shiftDown,再将根节点与倒数第二个节点交换,依次类推。代码如下:

/**
     * 堆排序
     * 时间复杂度:O(N*logN)
     * 空间复杂度:O(1)
     * 不稳定
     */
    public void heapSort(int[] arr){
        createHeap(arr);
        int end = arr.length-1;
        while(end > 0){
            swap(arr,0,end);
            shiftDown(arr,0,end);
            end--;
        }
    }
    private void shiftDown(int[] arr, int parent,int len) {
        int child = 2*parent+1;
        while(child < len){
            if(child+1 < len && arr[child] < arr[child+1]){
                child++;
            }
            if(arr[child] > arr[parent]){
                swap(arr,child,parent);
                parent = child;
                child = 2*parent+1;
            }else{
                break;
            }
        }
    }
    public void swap(int[] arr, int i, int j){
        int tmp = arr[i];
        arr[i] = arr[j];
        arr[j] = tmp;
    }
    public void createHeap(int[] array) {
        for (int parent = (array.length-2)/2; parent >= 0; parent--) {
            shiftDown(array,parent,array.length);
        }
    }

猜你喜欢

转载自blog.csdn.net/m0_74859835/article/details/132024358