优先级队列(堆)

堆概念的介绍

在之前的文章中介绍过,队列是一种先进先出(FIFO)的数据结构,但有些情况下,操作的数据可能带有优先级,一般出队列时,可能需要优先级高的元素先出队列。

在优先级队列PriorityQueue中,底层使用的是堆这种数据结构,而堆实际就是在完全二叉树的基础上进行了一些调整。每一个父亲节点都比子节点小称为小根堆,每一个父亲节点都比子节点大称为大根堆,如下图所示

堆的存储方式

在堆的概念中提到,堆是一个完全二叉树,在存储的过程中一般采用顺序存储的方式进行,例如有一个集合为{27,15,19,18,28,34,65,49,25,37},则在其堆中的存储的逻辑结构为

 

如上所示,在逻辑上为一个完全二叉树,如要将其调整为小根堆或大根堆(以大根堆为例),可以采用将根节点向上调整的方式

堆的创建

向上过程(以大堆为例)

1.让指针parent标记要调整的节点,指针child标记孩子节点中,大孩子的节点

2.将parent与child进行比较,如果child比parent大,则进行交换

循环如上的过程,完成后则调整为大根堆

代码如下

public class MyBigQueue {
    public int[] elem;          //采用数组的方式调整
    public int usesize;         //标志有效数字

    public MyBigQueue() {
        this.elem = new int[10];    //初试化数组大小为10,后期不够再扩容
    }

    public void initElem(int[] array){          //传一个数组初试化elem,并将elem调整为大根堆
        for (int i = 0; i < array.length; i++) {
            elem[i]=array[i];
            usesize++;
        }
    }

    public void CreatBigQ(){
        for(int parent=(usesize-1-1)/2;parent>=0;parent--){     //自下而上,确定父亲节点
            shifDown(parent,usesize);                       //将每个父亲节点放入方法中进行至下而上的调整
        }

    }

    public void shifDown(int parent,int end){           //写调整的方法
        int child = parent*2+1;
        while (child<end){                          //孩子节点只要没走到数组的结尾就继续遍历
            if(child+1<usesize&&elem[child]<elem[child+1]){
                child++;                                    //走完这一步,child走到了孩子节点的最大值下
            }
            if(elem[child]>elem[parent]){                   //判断是否需要交换
                swap(child,parent);                 //交换另写一个方法
                parent=child;                       //交换完后,
                child=child*2+1;                    //孩子节点走向孩子节点的左孩子节点
            }else {
                break;
            }
        }


    }
    public void swap(int x,int y){
        int tmp = elem[x];
        elem[x]=elem[y];
        elem[y]=tmp;
    }

在二叉树中,一个节点如果有左孩子节点,则左孩子节点的下标为2*X+1(X为当前节点下标),所以有上述代码 child=child*2+1;

验证

在传入数组arr后,elem为按顺序方式存储的大根堆

堆的删除

注意:堆的删除一点删除的是堆顶的元素

删除理论如下:

1.将堆顶元素对堆中最后一个元素交换
2.将堆的有效数据减少一个,也就是usesize-1;
3.将堆顶元素进行向下调整

代码如下

    public int poll(){
        int tmp = elem[0];    //保存要删除的头节点

        swap(0,usesize-1);    //将头节点和尾节点交换

        usesize--;            //将有效位数减一位

        shifDown(0,usesize);  //传入头节点,向下调整
        return tmp;
    }

验证

头节点置为末尾,有效位数减1,则再遍历时,遍历不到65,视为删除

堆的插入

插入理论如下:

1. 先将元素放入到底层空间中(注意:空间不够时需要扩容)
2.将最后新插入的节点向上调整,直到满足堆的性质

注意,在之前写的代码中,shifDown是向下调整的方法,向上调整需要要重写

代码如下

 public boolean isFull(){
        return usesize==elem.length;        //当有效位和数组的长度相同时,代表数组满了
    }

    public void offer(int val){
        if(isFull()){                   //判断数组是否满了,如果满了就进行扩容
            this.elem= Arrays.copyOf(elem,elem.length*2);
        }
        elem[usesize]=val;              //将新插入的数据置为数组最后
        usesize++;                      //有效位加1
        shifUp(usesize-1);          //进行向上调整,传入插入数据的下标,视为孩子节点

    }
    public void shifUp(int child){
        int parent = (child-1)/2;       //根据孩子节点确定父亲节点
        while (child>0){                //孩子节点还大于头节点的时候,就继续遍历
            if(elem[child]>elem[parent]){   //孩子节点的值大于父亲节点的值,就进行交换
                swap(child,parent);
                child=parent;           //将父亲节点给孩子,进行向上调整
                parent=(child-1)/2;     //父亲节点给自己的父亲节点
            }else {
                break;
            }
        }
    }

测试如下

全部的完整代码如下

import java.util.Arrays;

public class MyBigQueue {
    public int[] elem;          //采用数组的方式调整
    public int usesize;         //标志有效数字

    public MyBigQueue() {
        this.elem = new int[10];    //初试化数组大小为10,后期不够再扩容
    }

    public void initElem(int[] array){          //传一个数组初试化elem,并将elem调整为大根堆
        for (int i = 0; i < array.length; i++) {
            elem[i]=array[i];
            usesize++;
        }
    }

    public void CreatBigQ(){
        for(int parent=(usesize-1-1)/2;parent>=0;parent--){     //自下而上,确定父亲节点
            shifDown(parent,usesize);                       //将每个父亲节点放入方法中进行至下而上的调整
        }

    }

    public void shifDown(int parent,int end){           //写调整的方法
        int child = parent*2+1;
        while (child<end){                          //孩子节点只要没走到数组的结尾就继续遍历
            if(child+1<usesize&&elem[child]<elem[child+1]){
                child++;                                    //走完这一步,child走到了孩子节点的最大值下
            }
            if(elem[child]>elem[parent]){                   //判断是否需要交换
                swap(child,parent);                 //交换另写一个方法
                parent=child;                       //交换完后,
                child=child*2+1;                    //孩子节点走向孩子节点的左孩子节点
            }else {
                break;
            }
        }


    }
    public void swap(int x,int y){
        int tmp = elem[x];
        elem[x]=elem[y];
        elem[y]=tmp;
    }

    public int poll(){
        int tmp = elem[0];
        swap(0,usesize-1);
        usesize--;
        shifDown(0,usesize);
        return tmp;
    }
    public boolean isFull(){
        return usesize==elem.length;        //当有效位和数组的长度相同时,代表数组满了
    }

    public void offer(int val){
        if(isFull()){                   //判断数组是否满了,如果满了就进行扩容
            this.elem= Arrays.copyOf(elem,elem.length*2);
        }
        elem[usesize]=val;              //将新插入的数据置为数组最后
        usesize++;                      //有效位加1
        shifUp(usesize-1);          //进行向上调整,传入插入数据的下标,视为孩子节点

    }
    public void shifUp(int child){
        int parent = (child-1)/2;       //根据孩子节点确定父亲节点
        while (child>0){                //孩子节点还大于头节点的时候,就继续遍历
            if(elem[child]>elem[parent]){   //孩子节点的值大于父亲节点的值,就进行交换
                swap(child,parent);
                child=parent;           //将父亲节点给孩子,进行向上调整
                parent=(child-1)/2;     //父亲节点给自己的父亲节点
            }else {
                break;
            }
        }
    }

    public void getElem(){
        for (int i = 0; i < elem.length; i++) {
            System.out.println(elem[i]);
        }
    }


}

测试代码

public class Main  {
    public static void main(String[] args) {
        MyBigQueue myBigQueue = new MyBigQueue();
       int[] arr = {27,15,19,18,28,34,65,49,25,37};
       myBigQueue.initElem(arr);
       myBigQueue.CreatBigQ();
        System.out.println("=========");
        myBigQueue.poll();
        System.out.println("=========");
        myBigQueue.offer(99);
        myBigQueue.offer(78);
        myBigQueue.offer(108);
        System.out.println("=========");
        myBigQueue.getElem();




    }
}

相关推荐

  1. 优先级队列

    2024-07-11 22:06:02       37 阅读
  2. 算法:优先队列

    2024-07-11 22:06:02       36 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-11 22:06:02       67 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-11 22:06:02       72 阅读
  3. 在Django里面运行非项目文件

    2024-07-11 22:06:02       58 阅读
  4. Python语言-面向对象

    2024-07-11 22:06:02       69 阅读

热门阅读

  1. (C++哈希02) 四数相加 赎金信

    2024-07-11 22:06:02       22 阅读
  2. 超详细Python教程——面向对象相关知识

    2024-07-11 22:06:02       16 阅读
  3. 2024前端面试每日一更——简述MVVM?

    2024-07-11 22:06:02       22 阅读
  4. 呼叫中心遭遇UDP攻击,如何快速恢复服务?

    2024-07-11 22:06:02       23 阅读
  5. conda 重命名虚拟环境

    2024-07-11 22:06:02       21 阅读
  6. conda

    2024-07-11 22:06:02       20 阅读
  7. Facebook应用开发:认证与授权登录流程详解

    2024-07-11 22:06:02       22 阅读