Java 优先级队列(堆)

news2024/9/20 8:51:05

目录

    • 1. 优先级队列(堆)的概念
    • 2. 建立大根堆(向下调整算法)
    • 3. 堆插入元素(向上调整算法)
    • 4. 堆删除元素(向下调整算法)
    • 5. 优先级队列PriorityQueue的特性
    • 6. 优先级队列PriorityQueue的构造方法(默认小根堆)
    • 7. Java对象的比较
      • 1. euqals方法
      • 2. Comparable<>接口CompareTo方法(原类上实现)
      • 3. Compartor接口Compare方法(重写一个类实现)
      • 4. 三种方法的对比
    • 8. 大根堆的创建
    • 9. Top-k问题
      • 1.整体建小根堆
      • 2.局部建容量为k的大根堆

1. 优先级队列(堆)的概念

  1. 优先级队列PriorityQueue底层使用了堆这种数据结构,堆是一棵顺序存储的完全二叉树。
  2. 堆的性质
    ①堆中某个结点的值不大于/不小于其父节点的值(因为一旦称之为堆,则一定是大根堆or小根堆)
    ②堆是一棵完全二叉树(因为堆是顺序存储的二叉树,如果非完全二叉树就会存在null结点浪费了数组顺序存储的空间)
  3. 堆是一颗完全二叉树,采用按层编号进行顺序存储,十分的高效。那么假设i为结点在数组中的下标,则有:
    ①i的父结点下标为(i-1)/2
    ②i的左孩子下标为2i+1,i的右孩子下标为2i+2

2. 建立大根堆(向下调整算法)

思路:从最后一个非叶子结点开始进行向下调整,如果父节点的值已经满足大于两个孩子结点的值,则直接跳出循环调整下一个结点;否则就与孩子结点进行交换,交换完之后可能会造成子树不满足堆的性质,因此需要继续向下调整。
建堆的时间复杂度为O(N)
向下调整的时间复杂度为O(logN)

代码:

 //建立大根堆(向下调整)
    public void createHeap() {
        //从最后一个非叶子结点开始进行向下调整
        for (int i = (usedsize-1-1) / 2; i >= 0; i--) {
            shiftDown(i,usedsize);
        }
    }
    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 = child + 1;
            }

            if (elem[child] > elem[parent]) {
                int tmp = elem[child];
                elem[child] = elem[parent];
                elem[parent] = tmp;
                parent = child;
                child = 2 * parent + 1;
            }else {
                break;
            }
        }
    }

3. 堆插入元素(向上调整算法)

思路:堆插入元素是直接插在有效元素的后面的【注定是最后一个叶子结点】,然后对这个叶子结点所在的位置进行向上调整,注意这个叶子不用在跟其兄弟叶子进行比较,只用跟其父结点进行比较。
向上调整的时间复杂度为O(logN)
向上调整建堆的时间复杂度为O(NlogN)

因此一般选用上下调整的算法建堆,因为最后2层的结点最多,要调整的元素也多,时间会变慢。

代码:

 public void inset(int val) {
        if (isFull()) {
            addSize();
        }
        elem[usedsize++] = val;
        //向上调整算法
        shiftUp(usedsize-1);
    }
    public void shiftUp(int child) {
        int parent = (child - 1) / 2;
        while (parent >= 0) {
            if (elem[parent] < elem[child]) {
                int tmp = elem[child];
                elem[child] = elem[parent];
                elem[parent] = tmp;
                child = parent;
                parent = (child - 1) / 2;
            }else {
                break;
            }
        }
    }

    public boolean isFull() {
        if (elem.length == usedsize) {
            return true;
        }
        return false;
    }
    public void addSize() {
        elem = Arrays.copyOf(elem,elem.length*2);
    }

4. 堆删除元素(向下调整算法)

思路:每次删除的元素是堆顶的元素,将堆顶元素和堆中的最后一个元素进行交换,然后向下调整交换后的堆顶元素。
代码:

    public void swap(int i, int j) {
        int tmp = elem[i];
        elem[i] = elem[j];
        elem[j] = tmp;
    }
    public void deleElem() {
        if (usedsize <= 0) {
            return;
        }
        swap(0,usedsize-1);
        usedsize--;
        shiftDown(0,usedsize);
    }

5. 优先级队列PriorityQueue的特性

  1. PriorityQueue中放置的元素必须要可比较,否则就不能进行向上/下调整了,并且会抛出类型转换异常ClassCastException。
  2. 不能插入null对象,否则会抛出空指针异常NullPointerException。
  3. 插入和删除元素的时间复杂度是O(logN),即树的高度。
  4. PriorityQueue的底层使用堆数据结构,默认是小根堆,即每次获取到的元素都是最小的。

6. 优先级队列PriorityQueue的构造方法(默认小根堆)

  1. 无参构造方法实例化优先级队列对象
        PriorityQueue<Integer> priorityQueue = new PriorityQueue<>();

底层代码:
在这里插入图片描述
分析:会创建一个空的优先级队列,默认容量是11

  1. 传入一个整型参数实例化优先级队列对象
        PriorityQueue<Integer> priorityQueue = new PriorityQueue<>(100);

底层代码:
在这里插入图片描述
在这里插入图片描述
分析:会创建一个初始容量为initialCapacity的优先级队列,这里创建的容量是100,注意初始容量不能小于1,否则会抛出异常。

  1. 传入一个集合参数实例化优先级队列对象
        LinkedList<Integer> list = new LinkedList<>();
        list.add(1);
        list.add(2);
        list.add(3);
        PriorityQueue<Integer> priorityQueue = new PriorityQueue<>(list);

分析:用LinkedList对象来构造一个优先级队列的对象。

注意:实例化优先级对象默认创建的是小根堆,如果是大根堆需要提供比较器【涉及到对象的比较】。

7. Java对象的比较

在Java中,基本数据类型可以大于、小于、等于去比较,但是对于对象的比较则需要用到以下几种方法。

1. euqals方法

①如果自定义的类没有重写euqals方法,那么默认是继承的Object类中的equals方法,使用的是“==”。
在这里插入图片描述
②自定义类重写equals方法
在这里插入图片描述
注意:euqals只能按照相等进行比较,不能比较大于、小于。
测试:

        Student student1 = new Student("张三",22);
        Student student2 = new Student("张三",22);
        System.out.println(student1.equals(student2));

如果没有在Student类中重写euqals方法,则输出false;如果重写了euqals方法,则输出true。

2. Comparable<>接口CompareTo方法(原类上实现)

在这里插入图片描述
注意:
①在接口的后面要加上泛型
②这种写法侵入性比较强,不易修改

3. Compartor接口Compare方法(重写一个类实现)

class AgeComparator implements Comparator<Student> {

    @Override
    public int compare(Student o1, Student o2) {
        return o1.age - o2.age;
    }
}

实例化构造器对象,并且调用构造器中的compare方法。

    public static void main(String[] args) {
        Student student1 = new Student("张三",22);
        Student student2 = new Student("三",22);
        AgeComparator ageComparator = new AgeComparator();
        System.out.println(ageComparator.compare(student1, student2));
    }

4. 三种方法的对比

比较的方法说明
Object.equals不重写的话是继承Object的方法,需要重写覆写,只能比较相等与否
Comparable.compareTo需要手动实现接口,不够灵活
Comparator.compare需要实现一个比较器类对象

8. 大根堆的创建

优先级队列默认是小根堆,如果要创建大根堆,那么就传入比较方法。
比较器:(注意用到泛型)

class IntCompare implements Comparator<Integer> {

    @Override
    public int compare(Integer o1, Integer o2) {
        return o2 - o1;
    }
}

实例化比较器、优先级队列对象,并传入比较器参数

public class MaxHeap {
    public static void main(String[] args) {
        IntCompare intCompare = new IntCompare();
        PriorityQueue<Integer> priorityQueue = new PriorityQueue<>(intCompare);
        priorityQueue.offer(3);
        priorityQueue.offer(2);
        priorityQueue.offer(9);
        priorityQueue.offer(1);
        System.out.println(priorityQueue);
    }
}

运行结果:
在这里插入图片描述

9. Top-k问题

力扣 17.14. 最小K个数
设计一个算法,找出数组中最小的k个数。以任意顺序返回这k个数均可。
示例:
输入: arr = [1,3,5,7,2,4,6,8], k = 4
输出: [1,2,3,4]

1.整体建小根堆

思路:因为有大量的数据需要排序,因此采用堆这种数据结构,把所有的数据全部建成一个小根堆,然后每次取堆顶的元素。
代码:

class Solution {
    public int[] smallestK(int[] arr, int k) {
        int[] ret = new int[k];
        PriorityQueue<Integer> priorityQueue = new PriorityQueue<>();
        //小根堆
        for (int i = 0; i < arr.length; i++) {
            priorityQueue.offer(arr[i]);
        }
        for (int i = 0; i < k; i++) {
            ret[i] = priorityQueue.poll();
        }
        return ret;
    }
}

2.局部建容量为k的大根堆

思路:建立容量为k的大根堆,如果下一个待比较的元素比大根堆的元素小,就把大根堆的元素出堆,下一个待比较的元素进入堆中,这样最后得到的就是k个最小的元素。
【注意要判断数组为空 以及 k为0的情况,否则会出现空指针异常】
代码:

class IntComp implements Comparator<Integer> {

    @Override
    public int compare(Integer o1, Integer o2) {
        return o2 - o1;
    }
}

class Solution {
    public int[] smallestK(int[] arr, int k) {
        int[] ret = new int[k];
        if(arr.length == 0 || k == 0) {
            return ret;
        }
        //建立大根堆
        IntComp intComp = new IntComp();
        PriorityQueue<Integer> priorityQueue = new PriorityQueue<>(intComp);
        for (int i = 0; i < k; i++) {
            priorityQueue.offer(arr[i]);
        }
        for (int i = k; i < arr.length; i++) {
            if (arr[i] < priorityQueue.peek()) {
                priorityQueue.poll();
                priorityQueue.offer(arr[i]);
            }
        }
        for (int i = 0; i < k; i++) {
            ret[i] = priorityQueue.poll();
        }
        return ret;
    }
    
}

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

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

相关文章

单向环形链表和约瑟夫问题

单向环形链表和约瑟夫问题 Josephu 问题为&#xff1a;设编号为1&#xff0c;2&#xff0c;… n的n个人围坐一圈&#xff0c;约定编号为k&#xff08;1<k<n&#xff09;的人从1开始报数&#xff0c;数到m 的那个人出列&#xff0c;它的下一位又从1开始报数&#xff0c;数…

ubuntu压缩、归档 tar zip gzip的介绍

不同文件需要使用不同的工具进行压缩和解压&#xff0c;可以使用file文件查看是什么类型的文件 file 文件名如图&#xff0c;在第二行看到这个文件是gzip类型的。 文件&#xff1a;gzip 工具&#xff1a;gunzip gunzip是 gnu unzip的缩写。用来解压gzip压缩包。 解压命令 …

linux 内存管理

0.前言 1.了解Linux内存的管理机制(分段分页) 2.了解虚拟内存和物理内存的映射方式 3.了解操作系统内存与磁盘的交互&#xff08;分页机制---》缺页重读机制&#xff0c;用时拷贝机制) 4.应用程序如何高效使用内存和高级程序的设计方法 1.linux物理使用情况 内核区&#x…

动态站点地图提交百度收录

站点地图&#xff08;sitemap&#xff09;是一个网站的结构化数据&#xff0c;搜索引擎可以通过站点地图迅速了解一个网站的内容&#xff0c;加快搜索引擎收录。 一般来说&#xff0c;站点地图是以.xml结尾的静态化文件&#xff0c;例如个人博客和生化环材网的站点地图链接分别…

【Mysql】事务的四大特性(ACID)

【Mysql】事务的四大特性(ACID) 文章目录【Mysql】事务的四大特性(ACID)1. 概述1.1 并发事务问题1.1.1 脏读1.1.2 不可重复读1.1.3 幻读1.2 事务隔离级别1. 概述 事务的四大特性&#xff1a; 原子性&#xff08;Atomicity&#xff09;&#xff1a;事务是不可分割的最小操作单…

ROS学习寄录2

1 ROS核心概念 1.1 节点&#xff08;Node&#xff09; &#xff08;1&#xff09;执行具体任务的进程、独立运行的可执行文件 &#xff08;2&#xff09;不同节点可以使用不同的语言&#xff0c;可分布式运行在不同的主机 &#xff08;3&#xff09;节点在系统中的名称必须…

MODBUS总线的学习笔记

MODBUS学习记录 下面所有资料均copy于安富莱电子和博客中&#xff0c;仅作为个人学习笔记记录&#xff0c;写的不好请见谅。 1.modbus简介介绍 Modbus 是由 Modicon&#xff08;现为施耐德电气公司的一个品牌&#xff09;在 1979 年发明的&#xff0c;是全球第一个真正 用于…

PowerShell 学习笔记:操作XML文件

XML文件是有一定格式要求的文本文件。百度百科可扩展标记语言 (Extensible Markup Language, XML) &#xff0c;标准通用标记语言的子集&#xff0c;可以用来标记数据、定义数据类型&#xff0c;是一种允许用户对自己的标记语言进行定义的源语言。 XML是标准通用标记语言 可扩展…

adversarial Learning and attacks 学习笔记

GANs大家是比较熟悉的网络结构&#xff0c;adversarial attacks 也不是很新的概念了。近期的工作有涉及到adversarial attacks&#xff0c;故整理一下学习的内容。 Adversarial Attacks 对抗攻击样本&#xff1a;一张正常的大熊猫图片&#xff08;左图&#xff09;在被加入噪…

Leetcode.1828 统计一个圆中点的数目

题目链接 Leetcode.1828 统计一个圆中点的数目 题目描述 给你一个数组 points&#xff0c;其中 points[i] [xi, yi] &#xff0c;表示第 i个点在二维平面上的坐标。多个点可能会有 相同 的坐标。 同时给你一个数组 queries&#xff0c;其中 queries[j] [xj, yj, rj]&#x…

Feign入门

Feign入门Feign入门Feign代替RestTemplate自定义配置Feign使用优化最佳实践Feign入门 Feign代替RestTemplate RestTemplate方式调用存在的问题 先来看我们以前利用RestTemplate发起远程调用的代码&#xff1a; String url "http://userservice/user/" order.ge…

linux(Debian11)安装后安装无线网卡等驱动

在工作和生活中&#xff0c;我们经常会用到linux系统&#xff0c;debian作为一个老牌的程序员常用发行版&#xff0c;自然成为我们的首选。 下面记录一下&#xff0c;安装无线网卡的过程。 首先&#xff0c;可以通过命令查看自己所需要的驱动 lspci -vvv iwlwifi 就是我所需…

机器学习为什么使用归一化? 有哪些归一化算法?

一、归一化的基本介绍 1. Why 归一化? 一些分类器需要计算样本之间的距离(如欧氏距离),例如KNN。如果一个特征值域范围非常大,那么距离计算就主要取决于这个特征,从而与实际情况相悖(比如这时实际情况是值域范围小的特征更重要)。或者举一个更浅显的例子:对房子进行…

Java——组合总和(3)

题目链接 leetcode在线oj——组合总和&#xff08;3&#xff09; 题目描述 找出所有相加之和为 n 的 k 个数的组合&#xff0c;且满足下列条件&#xff1a; 只使用数字1到9 每个数字 最多使用一次 返回 所有可能的有效组合的列表 。该列表不能包含相同的组合两次&#xff…

计算机网络 —— TCP篇 TCP 重传、滑动窗口、流量控制、拥塞控制

计算机网络系列文章目录 TCP篇 TCP 重传、滑动窗口、流量控制、拥塞控制 文章目录计算机网络系列文章目录前言4.2 TCP 重传、滑动窗口、流量控制、拥塞控制4.2.1 重传机制超时重传什么情况会触发超时重传超时时间应该设置为多少呢&#xff1f;快速重传SACKD-SACK4.2.2 滑动窗…

【数据结构之二叉树系列】万字深剖数据结构---堆

目录前言一、堆二、堆的实现(重点)1. 数据类型重定义2. 堆结构的定义3. 堆结构的重定义三、堆中常见的基本操作&#xff08;重点&#xff09;1. 声明2. 定义&#xff08;1&#xff09;初始化&#xff08;2&#xff09;销毁&#xff08;3&#xff09;插入数据&#xff08;4&…

macOS Monterey 12.6.3 (21G419) 正式版 ISO、IPSW、PKG 下载

macOS Monterey 12.6&#xff0c;皆为安全更新&#xff0c;不再赘述。 macOS Monterey 12.6&#xff0c;发布于 2022 年 9 月 12 日&#xff08;北京时间今日凌晨&#xff09;&#xff0c;本次为安全更新。 今日&#xff08;2022-07-21&#xff09;凌晨&#xff0c;Apple 终于…

ATAC-seq分析:Peak Calling(8)

1. 寻找开发区域 ATACseq 的一个共同目标是识别转录因子结合和/或转录机制活跃的无核小体区域。该核小体游离信号对应于小于一个核小体的片段&#xff08;如 Greenleaf 论文中定义 < 100bp&#xff09;。 然而&#xff0c;为了识别开放的染色质&#xff0c;我们可以简单地使…

意想不到的结果:Foo(m)可能是在定义名为m的对象

文章目录例一&#xff1a;Foo(m); 是定义名为 m 的对象例二&#xff1a;Foo(m).i; 传入实参 m例三&#xff1a;func(Foo(m)); 传入实参 m例四&#xff1a;S(cout)(1) 定义名为 cout 的对象例五&#xff1a;S(std::cout)(1) 传入实参 std::cout你知道吗&#xff0c;如果 Foo 是…

vue3 watch 监听响应式数据变化

主要是用来监听ref 或者reactive 所包裹的数据才可以被监听到 <template><input type"text" v-model"message"> </template> <script setup lang"ts">import {ref, watch} from "vue";let message ref<s…