【Java 数据结构】-优先级队列以及Java对象的比较

news2025/4/8 12:56:36

作者:学Java的冬瓜
博客主页:☀冬瓜的主页🌙
专栏:【Java 数据结构】
分享:美妙人生的关键在于你能迷上什么东西。——《球状闪电》
主要内容优先级队列底层的堆,大堆的创建,插入,删除。堆的算法的时间复杂度。Java中的对象的比较。

在这里插入图片描述
在这里插入图片描述

文章目录

  • 一、模拟实现优先级队列
    • 1、什么是堆?
    • 2、代码
      • 2.1、建堆(大堆示例)
      • 2.2、堆的插入
      • 2.3、堆的删除
      • 2.4、获取堆顶元素
    • 3、小练习
    • 4、堆的总结
  • 二、算法的复杂度分析
    • 1、调整算法复杂度
    • 2、建堆的时间复杂度(重点)
  • 三、PriorityQueue关于比较的分析
    • 1、关于PriorityQueue的分析
    • 2、关于比较的分析
      • 法一:用equals方法
      • 法二:实现Compareable接口
      • 法三:比较器
        • <1> 制造比较器类
        • <2> 利用内部类完成比较器
      • 法四:lambda表达式(JDK1.8开始出现)

一、模拟实现优先级队列

JDK1.8中,PriorityQueue(优先级队列)底层使用了堆的数据结构。

1、什么是堆?

1> 堆是在完全二叉树的基础上变化得到的(取决于它的存储使用数组,如果是非完全二叉树,则浪费空间)。
2> 堆的特点:在每棵树(子树)中,都是根节点最大(大堆)或者根节点最小(小堆)。

2、代码

2.1、建堆(大堆示例)

建堆有两种方式:
第一种是从无到有(相当于第二种的插入元素,每次插入都调整为堆)
第二种是给一个数组,直接把它调整为堆。以下方法就是第二种。

public class MyHeap {
	// 1、堆的基本框架
    private int elem[];
    private int usedSize;
    private static final int DEFAULT_SIZE=11; // 注意:PriorityQueue源码中就是默认先开11个空间
    // 利用构造方法初始化
    public MyHeap() {
        elem = new int[DEFAULT_SIZE];
    }
    // 2、把数组放进堆数组
    public void initMyHeap(int[] arr){
        for(int i = 0; i < arr.length; i++){
            elem[i] = arr[i];
            usedSize++;
        }
    }
	// 3、建堆,以parent为根节点的树依次调整为大堆
    public void createHeap(){
        for (int parent = (usedSize-1-1)/2; parent >= 0; parent--) {  // 注意:数组下标到0
            shiftDown(parent, usedSize);
        }
    }

    // 向下调整算法
    private void shiftDown(int parent, int len){
        int child = parent*2+1;
        while(child < len){  // 注意:len-1为堆数组中最后一个元素的下标
            if(child+1 < len && elem[child] < elem[child+1]){// 左右孩子都满足下标在范围内。
                child++;// 找到以parent为根节点的左右孩子中大的
            }
			
            if(elem[child] > elem[parent]){
                int tmp = elem[child];
                elem[child] = elem[parent];
                elem[parent] = tmp;
				
				// 满足交换条件,就交换,然后继续往下判断,是否符合条件
                parent = child;
                child = parent*2+1;
            }else{
            	// 不满足交换条件,就说明已经满足大堆的要求,直接break
                break;
            }
        }
    }

2.2、堆的插入

    public int offer(int data){
    	// 1、插入元素到堆中
        if(heapIsFull()){
            grow();
        }
        elem[usedSize] = data;
        usedSize++;
		
		// 2、把最后一个元素通过向上调整放在对应位置
        shiftUp(usedSize-1); // 注意:usedSize-1才是data元素的下标

        return data;
    }
    // 向上调整算法
    private void shiftUp(int child){
        int parent = (child-1)/2;
        while(parent >= 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 heapIsFull(){
        return this.usedSize == this.elem.length;
    }
    // 扩容
    private void grow(){
        elem = Arrays.copyOf(this.elem, this.usedSize*2);
    }

2.3、堆的删除

	// 删除队头元素
    public int poll(){
        if(heapIsEmpty()){
            return -1;
        }
		// 把堆顶和
        int tmp = elem[0];
        elem[0] = elem[usedSize-1];
        elem[usedSize-1] = tmp;
        usedSize--;

        shiftDown(0,usedSize);

        return elem[usedSize];
    }
    // 堆空
    private boolean heapIsEmpty(){
        return this.usedSize == 0;
    }

2.4、获取堆顶元素

// 获取队头(堆顶)元素
    public int peek(){
        return elem[0];
    }
}

3、小练习

在这里插入图片描述

4、堆的总结

  • 建堆,有两种方法:法一是不断插入数据,不断调整为堆,法二是根据一个数组,直接调整为堆。下面都为法二的总结。
  • 建大堆:因为要求每棵子树都为大堆,1> 所以可以从调整最后一个节点的父节点为根节点的树,开始往上调整其它树。2> 每棵子树的调整都用向下调整算法:即左右孩子中大的节点比根节点还大,就把两者交换。
  • 堆的插入:1> 把新增元素先放在堆的完全二叉树的最后。 2> 使用向上调整算法,把堆里最后一个元素调整到正确的位置。
  • 堆的删除:1> 把堆顶元素和最后一个元素交换,然后堆的有效元素个数-1,2> 把堆顶元素使用向下调整算法放到正确位置。
  • 向下调整算法:建堆、删除堆顶元素都需要用到。要判断左右孩子谁大,大的再和父亲节点交换。然后再parent = child,child = parent*2+1继续往子树判断是否满足大堆,不满足则一直交换,直到child=有效元素个数(越界)。
  • 向上调整算法:插入元素用到向上调整算法,只是插入的这个child和它的parent比较,不满足大堆交换,直到child>0结束(或者parent>=0结束),不需要左右孩子比较。

二、算法的复杂度分析

1、调整算法复杂度

log(n)
1> 从代码上来看,向上调整算法和向下调整算法,都是孩子节点和父亲节点之间的交换,所以复杂度应该是树的高度
2> 又因为堆是完全二叉树,所以调整算法的复杂度(最坏)为log(n)

2、建堆的时间复杂度(重点)

在这里插入图片描述
在这里插入图片描述

三、PriorityQueue关于比较的分析

1、关于PriorityQueue的分析

  • 当没有传入数组容量的时候,默认数组大小是11.
  • 扩容时,当oldCapacity<64,就约为2倍扩容,当oldCapacity>=64,就为1.5倍扩容。
  • 当没有传入比较器的时候,你放进PriorityQueue里面的必须是可比较的(比如整形,或者比如Student类实现Compareable接口,重写compareTo方法)
  • 在PriorityQueue中整形数默认是小堆排序,如果想要实现大堆,可以使用比较器。不能实现Compareable重写compareTo方法,因为我不能改变Integer类的源码。

2、关于比较的分析

法一:用equals方法

equals方法只能判断两个元素是否相等,或者地址是否相等

法二:实现Compareable接口

让类实现Compareable接口,然后重写compareTo方法,在比较时,程序自动调用compareTo方法

public class Student implements Comparable<Student>{
    private String name;
    private int age;
    public Student(){
        
    }

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

法三:比较器

<1> 制造比较器类

//构造:让构造器类实现Comparator接口,重写compare方法
class AgeCmp implements Comparator<Student>{
    @Override
    public int compare(Student o1, Student o2) {
        return o1.age - o2.age; // 为了方便,这里的Student类的age字段,我没有封装
    }
}

//使用(main中):
PriorityQueue<Student> priorityQueue = new PriorityQueue<>(new AgeCmp());

<2> 利用内部类完成比较器

		//在main中
		// 利用内部类给构造器进行构造
		PriorityQueue<Integer> priorityQueue = new PriorityQueue<>(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2 - o1;
            }
        });

法四:lambda表达式(JDK1.8开始出现)

PriorityQueue<Integer> priorityQueue = new PriorityQueue<>((x,y)->{return x-y;});
//或者
PriorityQueue<Integer> priorityQueue = new PriorityQueue<>((x,y)->x.compareTo(y));
// 或者
PriorityQueue<Integer> priorityQueue = new PriorityQueue<>(Integer::compareTo);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/117981.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Openssl 生成自签名证书

最近在调试Ingress需要使用多份证书&#xff0c;对证书的生成和使用做了简单的整理。 不用翻垃圾桶一条过 #!/bin/sh output_dir"/opt/suops/k8s/ingress-files/certs/fanht-create-ssl/" read -p "Enter your domain [www.example.com]: " DOMAIN echo…

C++11特性-线程

并发 一个程序执行多个独立任务&#xff0c;提高性能 单核cpu是通过(任务切换)&#xff0c;即上下文切换&#xff0c;有时间开销 多核cpu(当核数>任务数)&#xff0c;硬件并发 进程 运行起来的一个可执行程序&#xff08;一段程序的运行过程&#xff09; 资源分配的最小单…

百数应用中心上新了——餐饮门店管理系统

随着智能化时代的来临&#xff0c;传统的餐饮门店管理方式逐渐暴露出缺陷。不少餐饮业的掌门人都纷纷对管理方式进行了转型&#xff0c;由传统模式转变为数字化系统的管理。然而数字化管理方式也没那么容易进行&#xff0c;想要百分百满足需求的系统耗时耗力耗钱&#xff0c;成…

不懂PO 设计模式?这篇实战文带你搞定 PO

1080442 73.1 KB 为UI页面写测试用例时&#xff08;比如web页面&#xff0c;移动端页面&#xff09;&#xff0c;测试用例会存在大量元素和操作细节。当UI变化时&#xff0c;测试用例也要跟着变化&#xff0c; PageObject 很好的解决了这个问题&#xff01; 使用UI自动化测试工…

钉钉 ANR 治理最佳实践 | 定位 ANR 不再雾里看花

作者&#xff1a;姜凡(步定) 本文为《钉钉 ANR 治理最佳实践》系列文章首篇《定位 ANR 不再雾里看花》&#xff0c;主要介绍了钉钉自研的 ANRCanary 通过监控主线程的执行情况&#xff0c;为定位 ANR 问题提供更加丰富的信息。 后续将在第二篇文章中讲述钉钉基于分析算法得出 …

【TuyaOS开发之旅】BK7231N GPIO的简单使用

接口讲解 GPIO初始化 /*** brief gpio 初始化* * param[in] pin_id: 需要初始化的GPIO编号&#xff0c; 对应TUYA_GPIO_NUM_E枚举* param[in] cfg: gpio 配置** return OPRT_OK on success. Others on error, please refer to tuya_error_code.h*/ OPERATE_RET tkl_gpio_ini…

基于SpringBoot工程开发Docker化微服务

目录 1. 微服务容器化治理的优缺点 1.1 微服务容器化的优点 1.2 微服务容器化的缺点 2. 微服务的两种模式 2.1 Microservice SDK 2.2 ServiceMesh 3. 微服务容器化治理的推荐模式 4.Windows下开发容器化微服务&#xff08;非K8S&#xff09; 4.1 开发环境 4.2 代码框架…

全网最新、最详细的使用burpsuite验证码识别绕过爆破教程(2023最新)

1、前沿 最近一直在研究绕过验证码进行爆破的方法&#xff0c;在这里对自己这段时间以来的收获进行一下分享。在这里要分享的绕过验证码爆破的方法一共有2个&#xff0c;分为免费版本&#xff08;如果验证码比较奇怪可能会有识别错误的情况&#xff09;和付费版本&#xff08;…

【Qt】QtCreator远程部署、调试程序

1、添加远程设备 1)QtCreator 工具–> 选项 --> 设备 --> 添加 2)设备设置向导选择–> Generic Linux Device --> 开启向导 3)填写“标识配置的名称”(随便写)、设备IP、用户名 --> 下一步 4)选择配对秘密文件,第一次配对,可以不填写,点击“下一…

嵌入式:ARM嵌入式系统开发流程概述

文章目录嵌入式开发的具体过程开发流程图嵌入式软件开发环境交叉开发环境远程调试结构图嵌入式应用软件开发的基本流程软件模拟环境目标板与评估板嵌入式软件开发的可移植性和可重用性嵌入式开发的具体过程 系统定义与需求分析阶段方案设计阶段详细设计阶段软硬件集成测试阶段…

Tomcat架构分析—— Engine

文章目录一、Tomcat的核心模块&#xff08;核心组件&#xff09;二、Engine 组件1.核心类与依赖图2.核心类源码分析构造函数&#xff1a;初始化方法 init&#xff1a;启动方法 start&#xff1a;3.Engine的启动过程总结一、Tomcat的核心模块&#xff08;核心组件&#xff09; …

机器学习之支持向量机(手推公式版)

文章目录前言1. 间隔与支持向量2. 函数方程描述3. 参数求解3.1 拉格朗日乘数3.2 拉格朗日对偶函数前言 支持向量机(Support(Support(Support VectorVectorVector Machine,SVM)Machine,SVM)Machine,SVM)源于统计学习理论&#xff0c;是一种二分类模型&#xff0c;是机器学习中获…

mysql查询当天,近一周,近一个月,近一年的数据

1.mysql查询当天的数据 select * from table where to_days(时间字段) to_days(now()); 2.mysql查询昨天的数据 select * from table where to_days(now( ) ) - to_days( 时间字段名) < 1 3.mysql查询近一周的数据 SELECT * FROM table WHERE date(时间字段) > D…

MySQL表的创建修改删除

目录 1、表的创建 2、查看表结构 3、表的修改 4、表的删除 1、表的创建 CREATE TABLE table_name ( field1 datatype, field2 datatype, field3 datatype ) character set 字符集 collate 校验规则 engine 存储引擎&#xff1b;说明&#xff1a; field 表示列名 datatype 表…

计算机系统基础实验 - 定点数加减法的机器级表示

实验序号&#xff1a;2 实验名称&#xff1a;定点数加减法的机器级表示 适用专业&#xff1a;软件工程 学 时 数&#xff1a;2学时 一、实验目的 1、掌握定点数加法的机器级表示。 2、掌握定点数减法的机器级表示。 3、掌握EFLAGS中4个牵涉到计算的标志位的计算方法。 4、掌握…

python实现动态柱状图

目录 一.基础柱状图 反转x轴&#xff0c;y轴&#xff0c;设置数值标签在右侧 小结 二.基础时间线柱状图 三.GDP动态柱状图绘制 1.了解列表的sort方法并配合lambda匿名函数完成列表排序 2.完成图表所需数据 3.完成GDP动态图表绘制 添加主题类型 设置动态标题 四.完整代码…

5.6 try语句块和异常处理

文章目录throw表达式(异常检测)try语句块&#xff08;异常处理&#xff09;编写处理代码函数在寻找处理代码的过程中退出标准异常异常是指存在于运行时的反常行为&#xff0c;这些行为超出了函数正常功能的范围。典型的异常包括失去数据库连接以及遇到意外输入等。当程序的某部…

Android Studio实现一个旅游课题手机app

文章目录&#xff1a; 目录 一、课题介绍 二、软件的运行环境 三、软件运行截图 四、软件项目总结 一、课题介绍 本次课题是实现了一个外出旅游的app&#xff0c;通过app可以显示景点的信息&#xff0c;以及根据地区查询&#xff0c;具体功能如下&#xff1a; 客户端 1.用…

【算法】面试题 - 数组(附讲解视频)

目录标题原地修改数组&#xff08;快慢指针&#xff09;26. 删除有序数组中的重复项扩展&#xff1a;83. 删除排序链表中的重复元素27. 移除元素283. 移动零左右指针167. 两数之和15. 三数之和[一个方法团灭 NSUM 问题](https://blog.csdn.net/yzx3105/article/details/1284606…

JavaWeb学生系统+教师系统+管理员系统

目录&#xff1a;一、前言&#xff1a;一、用到的技术&#xff1a;1.前端&#xff1a;HTMLCssJavaScriptAjaxJQueryBootStrap2.后端&#xff1a;ServletJSPSpringMVCJPA二、系统实现的效果&#xff1a;1.登录登出功能&#xff1a;(1)不同用户可以跳转到不同的系统页面。(2)设有…