算法沉淀——优先级队列(堆)(leetcode真题剖析)

在这里插入图片描述

算法沉淀——优先级队列

  • 01.最后一块石头的重量
  • 02.数据流中的第 K 大元素
  • 03.前K个高频单词
  • 04.数据流的中位数

优先队列(Priority Queue)是一种抽象数据类型,它类似于队列(Queue),但是每个元素都有一个关联的优先级。在优先队列中,元素按照优先级从高到低(或从低到高)排列,高优先级的元素先出队。这种数据结构可以用堆(Heap)来实现。

堆是一种二叉树结构,有两种主要类型:最大堆和最小堆。在最大堆中,每个节点的值都大于或等于其子节点的值;而在最小堆中,每个节点的值都小于或等于其子节点的值。对于优先队列来说,最大堆常常用于实现。

在堆中,根节点的元素具有最高(或最低)优先级,而且这一性质对于整个堆中的每个节点都成立。这确保了当我们从堆中移除元素时,总是移除具有最高(或最低)优先级的元素。

优先队列的常见操作包括:

  • 插入(Insertion):将元素插入队列中。
  • 删除最大(或最小)元素:移除并返回队列中具有最高(或最低)优先级的元素。

堆的实现可以通过数组或链表等数据结构。在使用数组实现堆时,父节点和子节点之间的关系可以通过数组的索引关系来表示。在C++中,可以使用 std::priority_queue 来实现优先队列,它默认使用最大堆,也可以通过传递自定义比较函数来实现最小堆。

01.最后一块石头的重量

题目链接:https://leetcode.cn/problems/last-stone-weight/

有一堆石头,每块石头的重量都是正整数。

每一回合,从中选出两块 最重的 石头,然后将它们一起粉碎。假设石头的重量分别为 xy,且 x <= y。那么粉碎的可能结果如下:

  • 如果 x == y,那么两块石头都会被完全粉碎;
  • 如果 x != y,那么重量为 x 的石头将会完全粉碎,而重量为 y 的石头新重量为 y-x

最后,最多只会剩下一块石头。返回此石头的重量。如果没有石头剩下,就返回 0

示例:

输入:[2,7,4,1,8,1]
输出:1
解释:
先选出 7 和 8,得到 1,所以数组转换为 [2,4,1,1,1],
再选出 2 和 4,得到 2,所以数组转换为 [2,1,1,1],
接着是 2 和 1,得到 1,所以数组转换为 [1,1,1],
最后选出 1 和 1,得到 0,最终数组转换为 [1],这就是最后剩下那块石头的重量。 

提示:

  • 1 <= stones.length <= 30
  • 1 <= stones[i] <= 1000

思路

我们可以每次拿出最大的两个相比较,有差值就将差值保留,相等就都粉碎,这样我们很容易想到使用堆去计算,因为这符合堆的特性,我们可以使用各语言库中的堆容器来计算。

  1. 创建最大堆(Max Heap): 通过 priority_queue<int> 创建一个默认为最大堆的优先队列,将给定的石头数组中的元素加入最大堆中。
  2. 迭代处理: 使用一个循环迭代处理,直到堆中剩余的元素个数小于等于1。
  3. 从堆中取出两个最大的元素,执行碎石操作: 每次取出堆中的两个最大元素(即石头的重量最大的两块),执行碎石操作,计算碎石后的重量,将结果加回堆中。
  4. 返回结果: 当循环结束时,堆中剩余的元素即为最后一块石头的重量,或者堆为空,返回相应的结果。

代码

class Solution {
public:
    int lastStoneWeight(vector<int>& stones) {
        priority_queue<int> heap;
        for(int x:stones) heap.push(x);

        while(heap.size()>1){
            int a=heap.top(); heap.pop();
            int b=heap.top(); heap.pop();
            if(a>b) heap.push(a-b);
        }

        return heap.size()?heap.top():0;
    }
};

02.数据流中的第 K 大元素

题目链接:https://leetcode.cn/problems/kth-largest-element-in-a-stream/

设计一个找到数据流中第 k 大元素的类(class)。注意是排序后的第 k 大元素,不是第 k 个不同的元素。

请实现 KthLargest 类:

  • KthLargest(int k, int[] nums) 使用整数 k 和整数流 nums 初始化对象。
  • int add(int val)val 插入数据流 nums 后,返回当前数据流中第 k 大的元素。

示例:

输入:
["KthLargest", "add", "add", "add", "add", "add"]
[[3, [4, 5, 8, 2]], [3], [5], [10], [9], [4]]
输出:
[null, 4, 5, 5, 8, 8]

解释:
KthLargest kthLargest = new KthLargest(3, [4, 5, 8, 2]);
kthLargest.add(3);   // return 4
kthLargest.add(5);   // return 5
kthLargest.add(10);  // return 5
kthLargest.add(9);   // return 8
kthLargest.add(4);   // return 8

提示:

  • 1 <= k <= 104
  • 0 <= nums.length <= 104
  • -104 <= nums[i] <= 104
  • -104 <= val <= 104
  • 最多调用 add 方法 104
  • 题目数据保证,在查找第 k 大元素时,数组中至少有 k 个元素

思路

这里是典型的topk问题,找的是第k个大的数,所以我们使用一个k大的小堆就能很好的解决这个问题。

在构造函数中,将初始的元素加入最小堆中,并维护堆的大小为 K,保证堆中只有前 K 大的元素。

当有新元素加入时,将新元素加入最小堆中,然后检查堆的大小是否超过 K,如果超过则弹出堆顶元素。最终返回当前堆顶元素,即为第 K 大的元素。这样,通过不断地将新元素加入最小堆,并保持堆的大小为 K,可以在 add 操作后始终保持堆中的元素为前 K 大的元素,而堆顶元素即为第 K 大的元素。

代码

class KthLargest {
    priority_queue<int,vector<int>,greater<int>> heap;
    int _k;
public:
    KthLargest(int k, vector<int>& nums) {
        _k=k;
        for(int& x:nums){
            heap.push(x);
            if(heap.size()>k) heap.pop();
        }
    }
    
    int add(int val) {
        heap.push(val);
        if(heap.size()>_k) heap.pop();
        return heap.top();
    }
};

/**
 * Your KthLargest object will be instantiated and called as such:
 * KthLargest* obj = new KthLargest(k, nums);
 * int param_1 = obj->add(val);
 */

03.前K个高频单词

题目链接:https://leetcode.cn/problems/top-k-frequent-words/

给定一个单词列表 words 和一个整数 k ,返回前 k 个出现次数最多的单词。

返回的答案应该按单词出现频率由高到低排序。如果不同的单词有相同出现频率, 按字典顺序 排序。

示例 1:

输入: words = ["i", "love", "leetcode", "i", "love", "coding"], k = 2
输出: ["i", "love"]
解析: "i" 和 "love" 为出现次数最多的两个单词,均为2次。
    注意,按字母顺序 "i" 在 "love" 之前。

示例 2:

输入: ["the", "day", "is", "sunny", "the", "the", "the", "sunny", "is", "is"], k = 4
输出: ["the", "is", "sunny", "day"]
解析: "the", "is", "sunny" 和 "day" 是出现次数最多的四个单词,
    出现次数依次为 4, 3, 2 和 1 次。

注意:

  • 1 <= words.length <= 500
  • 1 <= words[i] <= 10
  • words[i] 由小写英文字母组成。
  • k 的取值范围是 [1, **不同** words[i] 的数量]

**进阶:**尝试以 O(n log k) 时间复杂度和 O(n) 空间复杂度解决。

思路

很显然这里这是还是topk问题,还是使用堆来解决问题,但是这里有两个比较条件,所以我们要注意比较函数的实现,主逻辑是找出前k个出现次数最多的,所以整体我们建小堆,还有一个附属条件,次数相同时,字典序小的排在前面,所以在相同次数时,我们相对于字符串建大堆,将字母小的排在前面。

  1. 哈希表统计频率: 使用 unordered_map 哈希表记录每个字符串出现的频率。
  2. 优先队列: 使用一个自定义的比较函数对象 cmp 作为优先队列的比较规则。按照频率从大到小排序,若频率相同则按照字符串字典序从小到大排序。遍历哈希表,将每个键值对(字符串及其频率)加入最小堆,并保持堆的大小为 K,当堆的大小超过 K 时,弹出堆顶元素。
  3. 结果处理: 从堆中依次取出前 K 高的字符串,存储到结果数组中。

最终,返回存储前 K 高字符串的结果数组 ret。这样通过哈希表统计频率,并使用优先队列来维护前 K 高的频率,实现了找出频率前 K 高的字符串。

代码

class Solution {
    struct cmp{
        bool operator()(const pair<string,int>& a,const pair<string,int>& b){
            if(a.second==b.second) return a.first<b.first;
            return a.second>b.second;
        }
    };
public:
    vector<string> topKFrequent(vector<string>& words, int k) {
        unordered_map<string,int> hash;
        for(string& s:words) hash[s]++;

        priority_queue<pair<string,int>,vector<pair<string,int>>,cmp> heap;

        for(const pair<string,int>& x:hash){
            heap.push(x);
            if(heap.size()>k) heap.pop();
        }

        vector<string> ret(k);
        for(int i=k-1;i>=0;--i){
            ret[i]=heap.top().first;
            heap.pop();
        }

        return ret;
    }
};

04.数据流的中位数

题目链接:https://leetcode.cn/problems/find-median-from-data-stream/

中位数是有序整数列表中的中间值。如果列表的大小是偶数,则没有中间值,中位数是两个中间值的平均值。

  • 例如 arr = [2,3,4] 的中位数是 3
  • 例如 arr = [2,3] 的中位数是 (2 + 3) / 2 = 2.5

实现 MedianFinder 类:

  • MedianFinder() 初始化 MedianFinder 对象。
  • void addNum(int num) 将数据流中的整数 num 添加到数据结构中。
  • double findMedian() 返回到目前为止所有元素的中位数。与实际答案相差 10-5 以内的答案将被接受。

示例 1:

输入
["MedianFinder", "addNum", "addNum", "findMedian", "addNum", "findMedian"]
[[], [1], [2], [], [3], []]
输出
[null, null, null, 1.5, null, 2.0]

解释
MedianFinder medianFinder = new MedianFinder();
medianFinder.addNum(1);    // arr = [1]
medianFinder.addNum(2);    // arr = [1, 2]
medianFinder.findMedian(); // 返回 1.5 ((1 + 2) / 2)
medianFinder.addNum(3);    // arr[1, 2, 3]
medianFinder.findMedian(); // return 2.0

提示:

  • -105 <= num <= 105
  • 在调用 findMedian 之前,数据结构中至少有一个元素
  • 最多 5 * 104 次调用 addNumfindMedian

思路

这里我们最容易想到的暴力解法就是每次插入时排序,但是这种方法时间复杂度太高,其实这里我们可以使用一个大堆和一个小堆来维护所有数,各分一半数据,若两边长度相等,则返回两个堆顶相加除2的值,若不等,返回大堆堆顶即可。

在构造函数中初始化两个优先队列,left 用于存放较小的一半元素(最大堆),right 用于存放较大的一半元素(最小堆)。 在 addNum 方法中,根据元素的大小选择将其插入到左堆或右堆。插入后需要保持两个堆的大小差不超过 1,以确保中位数的计算。 在 findMedian 方法中,根据两个堆的大小关系返回中位数。通过这种方式,不断将数据插入到两个堆中,并保持它们的大小差不超过 1,就能够在 O(1) 时间内查找到当前数据流的中位数。

代码

class MedianFinder {
    priority_queue<int> left;
    priority_queue<int,vector<int>,greater<int>> right;
public:
    MedianFinder() {}
    
    void addNum(int num) {
        if(left.size()==right.size()){
            if(left.empty()||num<=left.top()) left.push(num);
            else{
                right.push(num);
                left.push(right.top());
                right.pop();
            }
        }
        else{
            if(num<=left.top()){
                left.push(num);
                right.push(left.top());
                left.pop();
            }else right.push(num);
        }
    }
    
    double findMedian() {
        if(left.size()==right.size()) return (left.top()+right.top())/2.0;
        return left.top();
    }
};

/**
 * Your MedianFinder object will be instantiated and called as such:
 * MedianFinder* obj = new MedianFinder();
 * obj->addNum(num);
 * double param_2 = obj->findMedian();
 */

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

WEB APIs(2)

应用定时器可以写一个定时轮播图&#xff0c;如下 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta http-equiv"X-UA-Compatible" content"IEedge"><meta name"viewport&qu…

如何在电脑和 SD 卡上恢复已删除 MOV等视频文件

MOV 是 Apple 创建的多媒体容器。您可能已经意识到&#xff0c;用 macOS QuickTime Player 录制的视频是以 MOV 格式保存的&#xff0c;而且 MOV 在 Windows 上也兼容。我们可能已经保存了很多 MOV 格式的视频。但是&#xff0c;如果这些 MOV 文件丢失或被意外删除怎么办&#…

Python二级考试笔记

Python二级考试笔记【源源老师】 01. 字符串 1. 常规功能合集 字符串本身有一些功能&#xff0c;有些之前运用过&#xff0c;这里总结如下&#xff1a; # 功能一&#xff1a;判断字符串类型 print(type("Hello")) print(str(123)) # 转换# 功能二&#xff1a;连…

OpenCV Mat实例详解 一

OpenCV中的Mat是一个类&#xff0c;它用存储图像信息。由两部分数据组成&#xff1a;矩阵头和像素值矩阵。矩阵头包含矩阵尺寸、存储方法、存储地址等信息&#xff0c;而像素值矩阵则存储实际的像素值数据。 Mat类在OpenCV中有十分重要的作用&#xff0c;图像信息的载入、保存、…

【知识图谱--第三讲知识图谱的存储与查询】

知识图谱的存储与查询 基于关系型数据库的知识图谱存储基于原生图数据库的知识图谱存储原生图数据库实现原理浅析 基于关系型数据库的知识图谱存储 基于原生图数据库的知识图谱存储 原生图数据库实现原理浅析

每日一题——LeetCode1437.是否所有1都至少相隔k个元素

方法一 两次遍历&#xff1a; 第一次遍历保存所有1的位置到res里&#xff0c;第二次遍历res检查是否所有相邻元素之间间隔都>k var kLengthApart function(nums, k) {let res[]for(let i0;i<nums.length;i){if(nums[i]1){res.push(i)}}for(let i1;i<res.length;i){…

Python 中实现线性搜索算法

前些天发现了一个巨牛的人工智能学习网站&#xff0c;通俗易懂&#xff0c;风趣幽默&#xff0c;忍不住分享一下给大家。点击跳转到网站零基础入门的AI学习网站~。 前言 线性搜索算法&#xff0c;也称为顺序搜索算法&#xff0c;是一种简单但常用的搜索技术&#xff0c;用于查…

VMwareWorkstation17.0虚拟机安装Windows2.03完整详细步骤图文教程

VMwareWorkstation17.0虚拟机安装Windows2.03完整详细步骤图文教程 第一篇 下载Windows2.03第二篇 配置Windows2.03虚拟机机器环境第三篇 启动Windows2.03系统 第一篇 下载Windows2.03 1.Windows2.0原版软盘下载地址是 暂不提供&#xff0c;后续更新 2.Windows2.03虚拟机镜像下…

P1228 地毯填补问题题解

题目 相传在一个古老的阿拉伯国家里&#xff0c;有一座宫殿。宫殿里有个四四方方的格子迷宫&#xff0c;国王选择驸马的方法非常特殊&#xff0c;也非常简单&#xff1a;公主就站在其中一个方格子上&#xff0c;只要谁能用地毯将除公主站立的地方外的所有地方盖上&#xff0c;…

MATLAB知识点:poissrnd函数(★★☆☆☆)生成泊松分布的随机数

讲解视频&#xff1a;可以在bilibili搜索《MATLAB教程新手入门篇——数学建模清风主讲》。​ MATLAB教程新手入门篇&#xff08;数学建模清风主讲&#xff0c;适合零基础同学观看&#xff09;_哔哩哔哩_bilibili 节选自第3章&#xff1a;课后习题讲解中拓展的函数 在讲解第三…

开源PDF工具 Apache PDFBox 认识及使用(知识点+案例)

文章目录 前言源码获取一、认识PDFBox二、导入依赖三、基础功能demo1&#xff1a;读取pdf所有内容demo2&#xff1a;读取所有页内容&#xff08;分页&#xff09;demo3&#xff1a;添加页眉、页脚demo4&#xff1a;添加居中45文字水印demo5&#xff1a;添加图片到右上角 参考文…

(四)【Jmeter】 JMeter的界面布局与组件概述

JMeter的界面布局 中文版&#xff1a; 英文版&#xff1a; JMeter的主界面包括菜单栏、工具栏、树形结构面板、视图面板等部分。 菜单栏&#xff1a;菜单栏包含了文件(File)、编辑(Edit)、查找(Search)、选项(Options)、工具(Tools)、帮助(Help)等菜单项&#xff0c;用于对…

WordPress作者页面链接的用户名自动变成16位字符串串插件Smart User Slug Hider

WordPress默认的作者页面URL链接地址格式为“你的域名/author/admin”&#xff0c;其中admin就是你的用户名&#xff0c;这样的话就会暴露我们的用户名。 为了解决这个问题&#xff0c;前面boke112百科跟大家分享了『如何将WordPress作者存档链接中的用户名改为昵称或ID』一文…

推荐在线图像处理程序源码

对于喜爱图像编辑的朋友们来说&#xff0c;Photoshop无疑是处理照片的利器。然而&#xff0c;传统的Photoshop软件不仅需要下载安装&#xff0c;还对电脑配置有一定的要求&#xff0c;这无疑增加了使用的门槛。 现在&#xff0c;我们为您带来一款革命性的在线PS修图工具——基…

紫微斗数双星组合:廉贞破军在卯酉

文章目录 前言内容总结 前言 紫微斗数双星组合&#xff1a;廉贞破军在卯酉 内容 紫微斗数双星组合&#xff1a;廉贞破军在卯酉 性格分析 廉贞星、破军星二星同宫&#xff0c;具有冒险开创的精神和领导能力&#xff0c;忍耐力强&#xff0c;工作积极稳重&#xff0c;冲劲大&a…

(17)Hive ——MR任务的map与reduce个数由什么决定?

一、MapTask的数量由什么决定&#xff1f; MapTask的数量由以下参数决定 文件个数文件大小blocksize 一般而言&#xff0c;对于每一个输入的文件会有一个map split&#xff0c;每一个分片会开启一个map任务&#xff0c;很容易导致小文件问题&#xff08;如果不进行小文件合并&…

软件实例分享,药店进销存软件医药系统进销存教程

软件实例分享&#xff0c;药店进销存软件医药系统进销存教程 一、前言 以下软件程序教程以 佳易王药店进销存管理系统V16.0为例说明 软件文件下载可以点击最下方官网卡片——软件下载——试用版软件下载 软件可以对药品的有效期进行管理&#xff0c;可以查询还有多少天到期的…

如何查找Windows的桌面文件夹?这里提供详细步骤

当你的电脑上有不同的用户时&#xff0c;Windows 11、10、…上的桌面文件夹或桌面目录特别有用&#xff0c;那么哪里才是真正的桌面文件夹目录。 自己的Windows桌面目录 1、启动Windows资源管理器 2、按F4键并输入%UserProfile% 3、点击桌面 这是你个人桌面的正确文件夹路径…

【数据分享】1980s~2020年青藏高原中分辨率土地覆被数据

各位同学们好&#xff0c;今天和大伙儿分享的是1980s~2020年青藏高原中分辨率土地覆被数据。如果大家有下载处理数据等方面的问题&#xff0c;您可以私信或评论。 吴炳方. (2023). 青藏高原中分辨率土地覆被数据&#xff08;1980s-2020&#xff09;. 国家青藏高原数据中心. 1 …

【STM32 CubeMX】I2C查询方式

文章目录 前言一、CubeMX配置IIC二、查询方式的使用2.1 分析一种情况2.2 Master模式2.3 Mem模式 总结 前言 在STM32 CubeMX环境中&#xff0c;I2C&#xff08;Inter-Integrated Circuit&#xff09;通信协议的查询方式是一种简单而常见的通信方式。通过查询方式&#xff0c;微…