当前位置: 首页 > news >正文

优先级队列(PriorityQueue)_1_模拟实现优先级队列

1、概念

        前面介绍过队列,队列是一种先进先出(FIFO)的数据结构 ,但有些情况下, 操作的数据可能带有优先级,一般出队 列时,可能需要优先级高的元素先出队列 ,这时候,使用队列显然不合适了。
        
        在这种情况下,数据结构应该提供两个最基本的操作,一个是返回最高优先级对象,一个是添加新的对象 。这种数据结构就是优先级队列 (Priority Queue)

2、优先级队列的模拟实现

        JDK1.8中的PriorityQueue底层使用了堆这种数据结构,而堆实际就是在完全二叉树的基础上进行了一些调整。

2.1 堆的概念 

        堆在逻辑上是一棵完全二叉树,物理上存储在数组中,满足根比子树的节点大称为大根堆,反之,根比子树节点小称为小根堆。堆的基本作用是:快速找到集合中的最值。

2.2 堆的存储方式

        从堆的概念可知, 堆是一棵完全二叉树,因此可以层序的规则采用顺序的方式来高效存储。
        注意:对于非完全二叉树,则不适合使用顺序方式进行存储,因为为了能够还原二叉树,空间中必须要存储空节点,就会导致空间利用率比较低。

        将元素存储在数组中后可以根据二叉树章节(二叉树 1-CSDN博客)讲解过的性质5进行还原。

假设i为节点在数组的下标,则有:

        1、若i==0,则i下标的节点是整棵树的根节点,否则i的双亲节点为(i-1)/2。

        2、若i有左孩子,则左孩子下标为2*i+1。

        3、若i有右孩子,则右孩子下标为2*i+2。

2.3 堆的创建 

        这是创建堆的所需成员变量和构造方法和堆的初始化:

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

    public TestHeap() {
        this.elem = new int[10];
    }
    public void initHeap(int[] array){
        for (int i = 0; i < elem.length; i++) {
            elem[i] = array[i];
            usedSize++;
        }
    }

        下面我们将创建一个大根堆:

        如下图,将一棵普通的完全二叉树,调整为了大根堆,其方法为,找到最下面的一棵子树,然后将其根结点与子树进行比较,调整每一棵子树根结点的位置,该方法称为向下调整

        首先我们找到最后一个父亲结点,需要用到我们刚才讲得性质5,公式为:(数组长度-1(也就是最后一个叶子节点)-1)/2。

        调整过程:设p是最后一个父亲结点的下标,那么p--就可以把每棵子树调整结束,再设置一个c来获取左右子树的最大值,根据大小根堆的性质分别进行调整。

public void creatHeap(){
        for (int parent = (usedSize-1-1) / 2; parent >= 0 ; parent--) {
            shiftDown(parent,usedSize);
        }
    }

        向下调整的方法:

    private void shiftDown(int parent,int usedSize){
        int child = (2*parent)+1;//左孩子
        //判断左孩子是否合法
        while(child < usedSize){
            if(child+1 < usedSize && elem[child] < elem[child+1]){//右孩子是否合法
                child++;
            }
            //child一定是左右孩子最大值的下标
            if(elem[child] > elem[parent]) {
                swap(child, parent);
                parent = child;
                child = 2*parent+1;
            }else{
                //本身已经是大根堆了
                break;
            }
        }
    }
    //交换元素
    private  void swap(int i,int j){
        int tmp = elem[i];
        elem[i] = elem[j];
        elem[j] = tmp;
    }

        向下调整创建堆的时间复杂度是O(N),如果使用向上调整时间复杂度是O(n*log2n)。

        下面是向下调整时间复杂的计算过程: 

    

 2.4 堆的插入

        插入要考虑两件事:
        1. 将元素往哪里放?(堆是否满? -- > 扩容)

        2. 放进去怎么放到合适的位置?

       将新插入的元素放入到最底层的空间中,空间不够的时候需要扩容,然后将最后插入的结点按照堆的性质向上调整。

 

 public  void offer(int val){
        if(isFull()){
            this.elem = Arrays.copyOf(elem,2*elem.length);
        }
        this.elem[usedSize] = val;//useSize=0
        //向上调整
        shiftUp(usedSize);
       usedSize++;
    }

    private void shiftUp(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;
            }
        }
    }

        时间复杂度:O(log2N)。 

2.5 堆的删除

        注意:这里删除的一定是堆顶元素。

        1、将堆顶元素和最后一个元素交换位置。

        2、useSized--。

        3、对堆顶元素进行向下调整。

        代码如下: 

   public  int poll(){
        int tmp = elem[0];
        //0下标元素和最后一个元素交换
        swap(0,usedSize-1 );
        //向下调整0~usedsize-1
        usedSize--;//删除
        shiftDown(0,usedSize);
        return tmp;
    }
 private void shiftDown(int parent,int usedSize){
        int child = (2*parent)+1;//左孩子
        //判断左孩子是否合法
        while(child < usedSize){
            if(child+1 < usedSize && elem[child] < elem[child+1]){//右孩子是否合法
                child++;
            }
            //child一定是左右孩子最大值的下标
            if(elem[child] > elem[parent]) {
                swap(child, parent);
                parent = child;
                child = 2*parent+1;
            }else{
                //本身已经是大根堆了
                break;
            }
        }
    }
    //交换元素
    private  void swap(int i,int j){
        int tmp = elem[i];
        elem[i] = elem[j];
        elem[j] = tmp;
    }

       时间复杂度:O(log2N)。

 2.6 获取堆顶元素

 public int peek(){
        return  this.elem[0];
    }
}

相关文章:

  • c++ 类和对象 —— 下 【复习总结】
  • Win11锁屏后显示“天气、市场、广告”如何取消显示
  • [从零开始学习JAVA] Stream流
  • 为什么Django能有效防御CSRF攻击?
  • 支持向量机(Support Vector Machine)基础知识2
  • linux 命令 cd
  • Centos搭建Tomcat服务器:我的实战经验分享(成功版本 详细!)
  • VMware中Ubuntu突然网络不可用,设置中网络设置消失?抱歉,发生错误。请联系软件提供商。需要运行NetworkManager,别急,3行代码带你搞定。
  • 音视频学习(二十九):YUV与RGB
  • 藻华自用资料四——无人机(ardupilot搭建)
  • 哈尔滨服务器租用托管流程
  • 【Java/数据结构】ArrayList的实现及使用
  • DeepSeek在学术研究方向初期工作提示词分享
  • linux操作系统3
  • Docker和containerd之概览(Overview of Docker and Containerd)
  • ISE 14.7 IP核 Block Memory Generator 更换coe
  • NoSQL数据库概述
  • 随机变量的不同收敛性
  • C++ 与 Qt 的内存管理机制
  • 逐次逼近型A/D原理
  • 古稀之年的设计家吴国欣:重拾水彩,触摸老上海文脉
  • 复旦一校友捐赠1亿元,却不留名
  • 从良渚到三星堆:一江水串起了5000年的文明对话
  • 哈马斯与以色列在多哈举行新一轮加沙停火谈判
  • 有关“普泽会”,俄官方表示:有可能
  • 盐城经济技术开发区党工委书记王旭东接受纪律审查和监察调查