锋盈数科-知识库 Logo
首页
软件开发
计算机基础
Hello Halo
新手必读
关于本知识库
登录 →
锋盈数科-知识库 Logo
首页 软件开发 计算机基础 Hello Halo 新手必读 关于本知识库
登录
  1. 首页
  2. 软件开发
  3. Java数据结构——Priority Queue

Java数据结构——Priority Queue

0
  • 软件开发
  • 发布于 2024-09-19
  • 0 次阅读
黄健
黄健

14天阅读挑战赛

文章目录

1.优先级队列

1.1概念

2.Priority Queue模拟实现

2.1堆的概念

2.2堆的存储方式

2.3创建堆

2.4堆的插入与删除

2.4.1堆的插入

2.4.2堆的删除

2.4.3 peek方法

3.常见习题

4.PriorityQueue的特性



1.优先级队列

1.1概念

队列是先进先出的数据结构,有些情况下数据是带有优先级的,一般出队列时,优先级高的元素先出队列

数据结构应该提供两个基本的数据结构,一个是返回最高优先级对象,一个是添加新的对象,这种数据结构就是优先级队列(Priority Queue)

2.Priority Queue模拟实现

PriorityQueue底层使用了堆的数据结构,而堆实际就是在完全二叉树的基础之上进行了一些元素的调 整

2.1堆的概念

1.是一种特殊的二叉树(完全二叉树)
2.通过数组的方式顺序存储
3.对于这个数的任意节点来说,满足根节点大于左右子树的值(大堆),或者任意一节点满足根节点的值小于左右子树的值(小堆)

2.2堆的存储方式

堆是一个完全二叉树,因此层序的规则采用顺序的方式存储比较高效

注意:对于非完全二叉树,则不适合使用顺序方式进行存储,因为为了能够还原二叉树,空间中必须要存储空节 点,就会导致空间利用率比较低

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

如果i为0,则i表示的节点为根节点,否则i节点的双亲节点为 (i - 1)/2

如果2 * i + 1 小于节点个数,则节点i的左孩子下标为2 * i + 1,否则没有左孩子

如果2 * i + 2 小于节点个数,则节点i的右孩子下标为2 * i + 2,否则没有右孩子

2.3创建堆

以大堆为例创建堆

数据为集合{ 27,15,19,18,28,34,65,49,25,37 }

元素存储到数组中:

还原成二叉树:

想要创建大堆,那么每颗子树都得是大根堆,现在需要在二叉树中进行调整

从最后一个子树开始调节,全都调整为大根堆

如何调整最后一个子树?

利用二叉树性质,最后一个节点和他的父亲节点下标关系为:(len - 1 - 1)/ 2,找到父亲节点然后自减后可以调整每个父亲节点

代码

test

public class Test {
    public static void main(String[] args) {
        TestHeap testHeap = new TestHeap();
        int[] array = {27,15,19,18,28,34,65,49,25,37};
        testHeap.initElem(array);
        testHeap.createHeap();
        System.out.println("++++++++++");
    }
}

TestHeap

class TestHeap{
    public int[] elem;
    public int usedSize;//有效数据的个数
    public static final int DEFAULT_SIZE = 10;
    public TestHeap(){
        this.elem = new int[DEFAULT_SIZE];
    }
    public void initElem(int[] array){
        for (int i = 0; i < array.length; i++) {
            elem[i] = array[i];
            usedSize++;
        }
    }
    public void createHeap(){
        for (int parent = (usedSize-1-1)/2; parent >= 0; parent--) {
            //调整方案
            shiftDown(parent,usedSize);
        }
    }

    /**
     * @param parent:根节点
     * @param len:子树调整的结束位置不能>len
     *要注意怎么判断什么时候没有子树的条件
     */
    private void shiftDown(int parent,int len){
        int child = 2*parent+1;
        while(child < len){
            //防止越界
            //child+1 < len &&保证有右孩子
            if(child+1 < len && elem[child] < elem[child+1]){
                child++;
            }
            //child一定最大值
            if (elem[child] > elem[parent]) {
                int tmp = elem[parent];
                elem[parent] = elem[child];
                elem[child] = tmp;
            //更新,当前子树也有左右子树,不更新只是调整了当前子树
                parent = child;
                child = 2*parent+1;
            }
            //当都为大根堆时直接跳出
            else{
                break;
            }
        }
    }
}

调试

可以看出已经创建了大根堆

最坏的情况即从根一路比较到叶子,比较的次数为完全二叉树的高度,即时间复杂度为log2(N)

2.4堆的插入与删除

2.4.1堆的插入

堆的插入总共需要两个步骤:

  1. 先将元素放入到底层空间中(注意:空间不够时需要扩容)

  2. 将最后新插入的节点向上调整,直到满足堆的性质

插入一个元素之后,仍然要保证这个堆是一个大根堆

插入后:


代码

//插入
    private void offer(int val){
        //判满
        if(isFull()){
            //扩容
           elem =  Arrays.copyOf(elem,2*elem.length);
        }
        //插入
        elem[usedSize] = val;
        //有效数据加一
        usedSize++;
        //调整为大根堆
        shiftUp(usedSize-1);

    }
    private void shiftUp(int child){
        int parent = (child-1)/2;
        //parent>=0也可作为条件,parent=0时也要进行调整
        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;
            }
        }
    }
    private boolean isFull(){
        //当有效数据=数组长度时就满了
        return usedSize == elem.length;
    }

调试

public class Test {
    public static void main(String[] args) {
        TestHeap testHeap = new TestHeap();
        int[] array = {27,15,19,18,28,34,65,49,25,37};
        testHeap.initElem(array);
        testHeap.createHeap();
        System.out.println("++++++++++");
        testHeap.offer(80);
    }
}

插入成功并且调整为大根堆

2.4.2堆的删除

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

具体如下:

  1. 将堆顶元素对堆中最后一个元素交换

  2. 将堆中有效数据个数减少一个

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

代码

 //删除
    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;
    }
    private boolean isEmpty(){
        return usedSize == 0;
    }

调试

删除前

删除后

2.4.3 peek方法

不为空直接返回elem[0]

 //peek
    public int peek(){
        if(isEmpty()){
            return -1;
        }
        return elem[0];
    }

3.常见习题

已知小根堆为8,15,10,21,34,16,12,删除关键字8之后需重建堆,在此过程中,关键字之间的比较次数是()A: 1B: 2C: 3D: 4

第一次比较:15和10 谁小

第二次比较:10和12谁小

第三次比较:12和16谁小

选c

4.PriorityQueue的特性

PriorityQueue的使用要注意:

  1. 使用时必须导入PriorityQueue所在的包,即:

    import java.util.PriorityQueue;

  2. PriorityQueue中放置的元素必须要能够比较大小,不能插入无法比较大小的对象,否则会抛出ClassCastException异常

  3. 不能插入null对象,否则会抛出NullPointerException

  4. 没有容量限制,可以插入任意多个元素,其内部可以自动扩容

  5. 插入和删除元素的时间复杂度为O(log2(N))

  6. PriorityQueue底层使用了堆数据结构

  7. PriorityQueue默认情况下是小堆—即每次获取到的元素都是最小的元素

建一个优先级队列并测试

public static void main(String[] args) {
        PriorityQueue<Integer> priorityQueue = new PriorityQueue<>();
        priorityQueue.offer(100);
        priorityQueue.offer(2);
        priorityQueue.offer(3);
        System.out.println(priorityQueue.poll());
    }

默认是小堆,则弹出最小的值2

当对象是引用类型时需要实现接口来比较

public class Test {
    public static void main(String[] args) {
        PriorityQueue<Student> priorityQueue = new PriorityQueue<>();
        priorityQueue.offer(new Student(10));
        priorityQueue.offer(new Student(12));
        System.out.println("+++++++++");

    }

}
class Student implements Comparable<Student>{
    public int age;
    public Student(int age) {
        this.age = age;
    }

    @Override
    public int compareTo(Student o) {
        return this.age-o.age;
    }
}

还是默认的小根堆

compareTo()方法是在那里被调用?我们看一下源码

也可以传比较器

```hljs
class IntCmp implements Comparator<Integer>{
    @Override
    public int compare(Integer o1, Integer o2) {
        return o2.compareTo(o1);
    }
}



原文链接: https://blog.csdn.net/chenchenchencl/article/details/127380833

标签: #JAVA 991 #软件开发 1171
相关文章

万字:支付“核心系统”详解 2024-11-02 15:33

专栏作者:隐墨星辰 \| 主编:陈天宇宙 这篇文章也尝试化繁为简,探寻支付系统的本质,讲清楚在线支付系统最核心的一些概念和设计理念。 虽然支付行业已经过了风头最劲的时光,但跨境支付仍然在蓬勃发展,每年依然有很多新人进入这个行业,这篇文章尝试为这些刚入行的新人提供一点帮助。 文章只介绍一些支付行业十几

资深支付架构师视角:实战从问题定义到代码落地的完整套路 2024-11-02 15:33

前言 今天从一个实际案例入手,介绍站在架构师的角度,如何识别并定义问题,提炼需求,技术方案选型,再到详细设计,最后利用AI的能力协助写出核心的代码,验证与调优。 解决问题存在一定的模式,也可以称之为框架,总结出自己的思考和解题框架,以后再碰到同类型的问题就可以如庖丁解牛一样容易。 很多年前,我写代码

Spring 实现 3 种异步接口 2024-10-18 09:07

大家好,我是苏三~ 如何处理比较耗时的接口? 这题我熟,直接上异步接口,使用 Callable、WebAsyncTask 和 DeferredResult、CompletableFuture等均可实现。 但这些方法有局限性,处理结果仅返回单个值。在某些场景下,如果需要接口异步处理的同时,还持续不断地

重学SpringBoot3-集成Redis(五)之布隆过滤器 2024-10-08 11:24

更多SpringBoot3内容请关注我的专栏:《SpringBoot3》 期待您的点赞👍收藏⭐评论✍ 重学SpringBoot3-集成Redis(五)之布隆过滤器 1. 什么是布隆过滤器? * 基本概念 适用场景 2. 使用 Redis 实现布隆过滤器 * 项目依赖 Redis 配置

设计模式第16讲——迭代器模式(Iterator) 2024-10-08 11:24

一、什么是迭代器模式 迭代器模式是一种行为型设计模式,它提供了一种统一的方式来访问集合对象中的元素,而不是暴露集合内部的表示方式。简单地说,就是将遍历集合的责任封装到一个单独的对象中,我们可以按照特定的方式访问集合中的元素。 二、角色组成 抽象迭代器(Iterator):定义了遍历聚合对象所需的方法

vue2路由和vue3路由区别及原理 2024-10-08 11:24

一、Vue2 与 Vue3 路由的区别 1. 创建路由实例方式的不同 Vue 2 中,通过 Vue.use() 注册路由插件,并通过 new VueRouter() 来创建路由实例。 import Vue from 'vue';import VueRouter from 'vue-router';i

目录

IT 外包服务商

  • 意见投递
  • zyf6619

软件开发应用

主菜单

  • 首页
  • 软件开发
  • 计算机基础
  • Hello Halo
  • 新手必读
  • 关于本知识库
Copyright © 2024 your company All Rights Reserved. Powered by Halo.