最大堆,最小堆,优先队列,堆排序 LC例题-找第K大元素

2024-06-05 15:12

本文主要是介绍最大堆,最小堆,优先队列,堆排序 LC例题-找第K大元素,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

LC215 数组中的第K个最大元素

class Solution {static Comparator<Integer> cmp = new Comparator<Integer>(){@Overridepublic int compare(Integer i1, Integer i2){return i1 - i2;//升序排列//  return i2 - i1;//降序}};
public static int findKthLargest(int[] nums, int k) {int len = nums.length;PriorityQueue<Integer> minTop = new PriorityQueue<>(k,cmp);for (int i = 0; i < k; i++){minTop.add(nums[i]);//add 添加元素}for (int i = k; i < len; i++){Integer topEle = minTop.peek();  // 返回队头元素(不删除),失败时前者//peek取出队头元素,不删除// 只要当前遍历的元素比堆顶元素大,堆顶弹出,遍历的元素进去(把最小的元素排出去)if (nums[i] > topEle){minTop.poll();  // 获取队头元素并删除,并返回,失败时前者抛出null,minTop.offer(nums[i]);  // 在队尾插入元素,插入失败时抛出false,并}}return minTop.peek();//(k个里面最小的,就是第k大的)}   
}

定义:

最大堆是一种完全二叉树,其中每个节点的值都大于等于其子节点的值(也就是根节点是最大的),除根节点外每个节点都满足父节点的值大于等于其子节点的堆属性。(大根堆)

最小堆也是一种完全二叉树,其中每个节点的值都小于等于其子节点的值(也就是根节点是最小),除根节点外每个节点都满足父节点的值小于等于其子节点的堆属性。

应用:

堆排序是一种利用堆的数据结构进行排序的算法,nlogn

优先队列:常用于资源调度,任务调度

中位数维护:使用两个堆来维护数据流中的中位数,一个堆最大堆存数据流的前半部分,一个最小堆存数据流后半部分。

最大堆(优先队列)的删除

最大堆的操作先从简单的来,最大堆的设计就是为了方便将最大元素或最小元素以最小的代价给出。

从上面的树中,删除一个最大元素将使得根结点被弹出。需要对根结点经行补全,此时要寻找最大堆中剩余节点中的最大元素补到根结点的位置,也就是数组中下标为1的位置。

很显然,下面的元素中,最大元素必然是根节点的左右子树中较大的那一个。

我们将根节点补充为其较大的那一个儿子。而这个被拿去补充根节点的元素所占的位置仍然需要其较大的子结点去补充,这样不断向下,直到被拿去补充的元素不再拥有左右子树,退出循环。

最大堆(优先队列)的插入

最大的的插入与删除类似于反向操作。

我们首先将要插入的数据放在最大堆的尾部,然后比较其父亲子树与该结点的参数大小,若插入的参数大于其父亲结点的参数,则二者做交换。在不断向上交换的过程中,循环什么时候结束呢?

没错,当比较的元素N/2为0,我们先前定的Flag时,则退出循环,若是最大堆可以将Flag定位无穷大,反之可以定为0或无穷小。

优先队列(Java的PriorityQueue)

PriorityQueue实现了Queue接口,可以存放基本数据类型的包装类或自定义的类(前提是都可排序,必须实现Comparable接口,并根据需要重写CompareTo方法)。

对于基本数据类型的包装类,优先队列中元素默认排列顺序是升序排列。

对于自定义的类,需要定义比较器。

请注意,此实现不同步。 如果任何线程修改队列,多线程不应同时访问PriorityQueue实例。 而是使用线程安全的PriorityBlockingQueue

Comparator<Object> cmp = new Comparator<Object>() {

public int compare(Object o1, Object o2) {

//升序

return o1-o2;

//降序

return o2-o1;

}

};

可以使用以下方法来操作优先队列

insert(key):向优先队列中插入一个元素,时间复杂度为O(logN)。

deleteMin():删除并返回优先队列中优先级最高的元素,时间复杂度为O(logN)。

getMin():返回优先队列中优先级最高的元素,但不进行删除,时间复杂度为O(1)。

add(元素)或offer(元素):向队列中添加元素。

remove()或poll():移除并返回队列中的第一个(最高优先级)元素。

peek():返回队列中的第一个(最高优先级)元素,但不进行移除。

isEmpty():检查队列是否为空。

size():返回队列中的元素个数。

默认情况下,优先队列中的元素按照自然顺序进行排序。但也可以通过传递一个自定义的Comparator对象来指定元素的比较规则。

关于PriorityQueue的使用要注意:

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

import java.util.PriorityQueue;

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

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

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

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

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

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

默认情况下,PriorityQueue队列是小堆(即队首是最小元素,升序),如果需要大堆需要用户提供比较器

// 用户自己定义的比较器:直接实现Comparator接口,然后重写该接口中的compare方法即可

class IntCmp implements Comparator<Integer>{

@Override

public int compare(Integer o1, Integer o2) {

return o2-o1;

}

}

public class TestPriorityQueue {

public static void main(String[] args) {

PriorityQueue<Integer> p = new PriorityQueue<>(new IntCmp());

p.offer(4);

p.offer(3);

p.offer(2);

p.offer(1);

p.offer(5);

System.out.println(p.peek());

}

}

常用方法:

如果容量小于 64 时,是按照 oldCapacity 的 2 倍方式扩容的

如果容量大于等于 64 ,是按照 oldCapacity 的 1.5 倍方式扩容的

如果容量超过 MAX_ARRAY_SIZE ,按照 MAX_ARRAY_SIZE 来进行扩容

优先队列应用

任务调度:在多线程或分布式系统中,任务调度是一个重要的问题。优先队列可以根据任务的优先级来决定执行顺序,高优先级的任务会被首先执行。

搜索算法:在图搜索、最短路径、A*算法等问题中,优先队列可以用来存储待探索的节点,并按照启发式函数的值(估计到目标的距离)进行排序,以便选择下一个最有希望的节点进行探索。

带有时间限制的调度问题:在某些场景下,任务可能有截止时间。优先队列可以根据任务的截止时间来确定执行顺序,确保在截止时间前完成最高优先级的任务。

数据压缩:在哈夫曼编码等数据压缩算法中,优先队列可以用来存储字符及其出现频率,然后构建压缩树。

操作系统调度:操作系统中的进程调度也可以使用优先队列来实现,根据进程的优先级来决定调度顺序。

参考:chatGPT

【Java】PriorityQueue--优先级队列_java priorityqueue-CSDN博客

学习一波Java语言中的优先队列 PriorityQueue_java 优先队列-CSDN博客

Java优先队列PriorityQueue使用详解_java priorityqueue用法-CSDN博客

数据结构学习之——最大堆、最小堆(优先队列、哈夫曼树)_最大堆和最小堆原理-CSDN博客

这篇关于最大堆,最小堆,优先队列,堆排序 LC例题-找第K大元素的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!


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

相关文章

golang实现延迟队列(delay queue)的两种实现

《golang实现延迟队列(delayqueue)的两种实现》本文主要介绍了golang实现延迟队列(delayqueue)的两种实现,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的... 目录1 延迟队列:邮件提醒、订单自动取消2 实现2.1 simplChina编程e简单版:go自带的time

Java的栈与队列实现代码解析

《Java的栈与队列实现代码解析》栈是常见的线性数据结构,栈的特点是以先进后出的形式,后进先出,先进后出,分为栈底和栈顶,栈应用于内存的分配,表达式求值,存储临时的数据和方法的调用等,本文给大家介绍J... 目录栈的概念(Stack)栈的实现代码队列(Queue)模拟实现队列(双链表实现)循环队列(循环数组

Redis消息队列实现异步秒杀功能

《Redis消息队列实现异步秒杀功能》在高并发场景下,为了提高秒杀业务的性能,可将部分工作交给Redis处理,并通过异步方式执行,Redis提供了多种数据结构来实现消息队列,总结三种,本文详细介绍Re... 目录1 Redis消息队列1.1 List 结构1.2 Pub/Sub 模式1.3 Stream 结

SpringKafka错误处理(重试机制与死信队列)

《SpringKafka错误处理(重试机制与死信队列)》SpringKafka提供了全面的错误处理机制,通过灵活的重试策略和死信队列处理,下面就来介绍一下,具有一定的参考价值,感兴趣的可以了解一下... 目录引言一、Spring Kafka错误处理基础二、配置重试机制三、死信队列实现四、特定异常的处理策略五

如何高效移除C++关联容器中的元素

《如何高效移除C++关联容器中的元素》关联容器和顺序容器有着很大不同,关联容器中的元素是按照关键字来保存和访问的,而顺序容器中的元素是按它们在容器中的位置来顺序保存和访问的,本文介绍了如何高效移除C+... 目录一、简介二、移除给定位置的元素三、移除与特定键值等价的元素四、移除满足特android定条件的元

C++从序列容器中删除元素的四种方法

《C++从序列容器中删除元素的四种方法》删除元素的方法在序列容器和关联容器之间是非常不同的,在序列容器中,vector和string是最常用的,但这里也会介绍deque和list以供全面了解,尽管在一... 目录一、简介二、移除给定位置的元素三、移除与某个值相等的元素3.1、序列容器vector、deque

C++常见容器获取头元素的方法大全

《C++常见容器获取头元素的方法大全》在C++编程中,容器是存储和管理数据集合的重要工具,不同的容器提供了不同的接口来访问和操作其中的元素,获取容器的头元素(即第一个元素)是常见的操作之一,本文将详细... 目录一、std::vector二、std::list三、std::deque四、std::forwa

Spring Boot整合消息队列RabbitMQ的实现示例

《SpringBoot整合消息队列RabbitMQ的实现示例》本文主要介绍了SpringBoot整合消息队列RabbitMQ的实现示例,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的... 目录RabbitMQ 简介与安装1. RabbitMQ 简介2. RabbitMQ 安装Spring

使用C++实现链表元素的反转

《使用C++实现链表元素的反转》反转链表是链表操作中一个经典的问题,也是面试中常见的考题,本文将从思路到实现一步步地讲解如何实现链表的反转,帮助初学者理解这一操作,我们将使用C++代码演示具体实现,同... 目录问题定义思路分析代码实现带头节点的链表代码讲解其他实现方式时间和空间复杂度分析总结问题定义给定

如何通过Python实现一个消息队列

《如何通过Python实现一个消息队列》这篇文章主要为大家详细介绍了如何通过Python实现一个简单的消息队列,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录如何通过 python 实现消息队列如何把 http 请求放在队列中执行1. 使用 queue.Queue 和 reque