【算法】使用优先级队列(堆)解决算法题(TopK等)(C++)

news2024/11/15 3:59:54

文章目录

  • 1. 前言
  • 2. 算法题
    • 1046.最后一块石头的重量
    • 703.数据流中的第K大元素
  • 2.5 如何选择大根堆 与 小根堆? + 为什么选择大根堆(小根堆)?
    • 692.前K个高频单词
    • 295.数据流的中位数

1. 前言

我们知道:优先级队列是一种常用的数据结构,用于解决许多算法问题。基于堆(Heap)实现,在每次操作中能够快速找到最大或最小值

使用优先级队列的典型算法问题包括:

  • Top K 问题:查找列表中前 K 个最大或最小的元素。
  • 合并 K 个排序数组:将 K 个已排序的数组合并为一个有序数组。
  • Dijkstra 算法:在加权图中找到从起点到目标节点的最短路径。
  • Huffman 编码:使用最小堆构建前缀编码树来压缩数据。

下面会挑选一些算法题并使用优先级队列进行解题。


2. 算法题

1046.最后一块石头的重量

在这里插入图片描述
思路

  • 解法大根堆
  • 大根堆:每个节点都大于等于其子节点,则堆顶节点为最大的
    1. 将数组中所有元素加入堆中,并进行循环,直至堆为空
    2. 循环每次 取两次堆顶元素,即当前最重的两石头
    3. 将两元素差继续入堆,重复过程直至循环结束
      • 如果堆中还剩一个元素,返回该元素
      • 如果已经没有元素,返回0

代码

int lastStoneWeight(vector<int>& stones) {
    priority_queue<int> heap; // 创建大根堆
    // 将数组所有元素添加到堆中
    for(int stone : stones) heap.push(stone);

    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;
}

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

在这里插入图片描述

思路

  • 题意分析:根据题目,可以看出来该题是一道topK类型题
  • 解法全局变量 + 小根堆
    • 使用全局变量可以省去函数之间传参的过程,也方便编写代码
    1. 创建全局变量标记k和创建全局小根堆
      • 关于为什么选择小根堆,可以看后面的解释。
    2. 由于add函数要求添加数字后返回第k大的元素,对于构造函数KthLargest,我们直接将数组中前k大的元素插入
    3. 对于add函数,直接将val插入到堆中并判断是否堆内元素超出k个
      • 如果超出,则pop掉,后直接返回堆顶元素(即为第K大)

2.5 如何选择大根堆 与 小根堆? + 为什么选择大根堆(小根堆)?

在这里插入图片描述

代码

class KthLargest {
public:
    // 小根堆
    priority_queue<int, vector<int>, greater<int>> heap;
    int _k;

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

692.前K个高频单词

在这里插入图片描述

思路

  • 题意分析:即返回数组中出现次数最多的字符串(单词)
  • 解法哈希表 + 优先级队列
    1. 哈希表统计每个单词的出现次数
    2. 根据题目要求,当单词的频率相同时,按照字典序排列,则我们自定义优先级队列的比较函数。则:
      • 当单词频率不同时,用小堆的比较方式
      • 当单词频率相同时,按照字典序,用大堆的比较方式
    3. 将哈希表中统计的前k高的 字母以及频率 加入到队列
    4. 最后返回结果,遍历堆,每次加入到结果集result中并pop即可。

代码

vector<string> topKFrequent(vector<string>& words, int k) {
    // 统计单词出现频率
    unordered_map<string, int> freq;
    for (const string& word : words) {
        freq[word]++;
    }
    
    // 自定义优先队列的比较函数
    auto cmp = [](const pair<string, int>& a, const pair<string, int>& b) {
        // 比较出现次数,如果相同则按照字母顺序
        return a.second > b.second || (a.second == b.second && a.first < b.first);
    };
    
    // 优先队列,默认是大顶堆,用于存储频率最高的 k 个单词
    priority_queue<pair<string, int>, vector<pair<string, int>>, decltype(cmp)> pq(cmp);
    
    // 遍历统计好的频率,将单词加入优先队列
    for (const auto& entry : freq) {
        pq.push(entry);
        if (pq.size() > k) {
            pq.pop(); // 如果队列大小超过 k,则弹出频率最小的单词
        }
    }
    
    // 从优先队列中取出结果
    vector<string> result(k);
    for (int i = k - 1; i >= 0; --i) {
        result[i] = pq.top().first; // 逆序存储结果
        pq.pop();
    }
    
    return result;
}

295.数据流的中位数

在这里插入图片描述

思路

  • 题意分析:题目要求实现一个类,类中包含一个构造函数、一个add函数用于添加元素、以及一个find函数

  • 解法一排序 sort
    在这里插入图片描述

    • 对于本题,使用该排序法是会超时的
  • 解法二插入排序的思想
    在这里插入图片描述

    • 插入排序思想解本题是有可能超时的,但依然需要了解这种解题思想。
  • 解法三大小堆维护
    在这里插入图片描述
    在这里插入图片描述

    • 上图解释了方法思路,具体细节看下面代码即可。

代码

class MedianFinder {
public:
    // 大小堆,左大堆,右小堆
    // 且当共有奇数个元素时,左存多一个元素
    priority_queue<int, vector<int>> left;
    priority_queue<int, vector<int>, greater<int>> right;

    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() {
        return (left.size() == right.size()) ? (left.top() + right.top()) / 2.0 : left.top();
    }
};

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

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

相关文章

C# .NET读取Excel文件并将数据导出到DataTable、数据库及文本

Excel文件是存储表格数据的普遍格式&#xff0c;因此能够高效地读取和提取信息对于我们来说至关重要。C#语言借助.NET Framework和各种库的广泛功能&#xff0c;能够进行高效的数据操作。利用C#读取Excel文件并将数据写入数据库和DataTable&#xff0c;或者将数据用于其他目的&…

Rust基础语法1

所有权转移&#xff0c;Rust中没有垃圾收集器&#xff0c;使用所有权规则确保内存安全&#xff0c;所有权规则如下&#xff1a; 1、每个值在Rust中都有一个被称为其所有者&#xff08;owner&#xff09;的变量&#xff0c;值在任何时候只能有一个所有者。 2、当所有者离开作用域…

一文读懂「Fine-tuning」微调

一、什么是微调&#xff1f; 1. 什么是微调&#xff1f; 微调是指在预训练模型&#xff08;Pre-trained model&#xff09;的基础上&#xff0c;针对特定任务或数据领域&#xff0c;对部分或全部模型参数进行进一步的训练和调整&#xff08;Fine Tune&#xff09;。预训练模型…

03 MyBatisPlus之条件构造器Wrapper+三个核心注解

2. 条件构造器 2.1 条件构造器作用 //创建一个查询条件构造器对象,所有条件都放进去 QueryWrapper<User> queryWrapper new QueryWrapper<>(); queryWrapper.eq("name", "John"); // eq添加等于条件 queryWrapper.ne("age", 30);…

伊恩·斯图尔特《改变世界的17个方程》波动方程笔记

主要是课堂的补充&#xff08;yysy&#xff0c;我觉得课堂的教育模式真有够无聊的&#xff0c;PPT、写作业、考试&#xff0c;感受不到知识的魅力。 它告诉我们什么&#xff1f; 小提琴琴弦上某个小段的加速度&#xff0c;与相邻段相对于该段的平均位移成正比。 为什么重要&…

初阶数据结构:顺序表

目录 1. 引子&#xff1a;线性表2. 简单数据结构&#xff1a;顺序表2.1 顺序表简介与功能模块分析2.2 顺序表的实现2.2.1 顺序表&#xff1a;存储数据结构的构建2.2.2 顺序表&#xff1a;初始化与空间清理&#xff08;动态&#xff09;2.2.3 顺序表&#xff1a;插入与删除数据2…

总结网络中的一些基本概念

1. IP地址 描述一个设备在网络上的位置&#xff0c;而且计算机是通过数字来描述IP地址的。例如&#xff08;生活中的地址&#xff09; 2. 端口号 描述一个主机上的哪个应用程序&#xff0c;有了IP可以确定主机&#xff0c;但是一个主机上可能有很多程序在使用网络&#xff0c;…

基于SpringBoot Vue自习室管理系统

大家好✌&#xff01;我是Dwzun。很高兴你能来阅读我&#xff0c;我会陆续更新Java后端、前端、数据库、项目案例等相关知识点总结&#xff0c;还为大家分享优质的实战项目&#xff0c;本人在Java项目开发领域有多年的经验&#xff0c;陆续会更新更多优质的Java实战项目&#x…

Elasticsearch各种高级文档操作3

本文来记录几种Elasticsearch的文档操作 文章目录 初始化文档数据聚合查询文档概述对某个字段取最大值 max 示例对某个字段取最小值 min 示例对某个字段求和 sum 示例对某个字段取平均值 avg 示例对某个字段的值进行去重之后再取总数 示例 本文小结 初始化文档数据 在进行各种文…

ASP.NET Core 对象池化技术

写在前面 Microsoft.Extensions.ObjectPool 是 ASP.NET Core 基础结构的一部分&#xff0c;当对象的初始化成本较高&#xff0c;并且可能被频繁使用时&#xff0c;才适合采用对象池技术&#xff1b;被ObjectPool管理的对象不会进入垃圾回收&#xff0c;使用时通过由实例对象实…

【迅搜19】扩展(二)TNTSearch和JiebaPHP方案

扩展&#xff08;二&#xff09;TNTSearch和JiebaPHP方案 搜索引擎系列的最后一篇了。既然是最后一篇&#xff0c;那么我们也轻松一点&#xff0c;直接来看一套非常有意思的纯 PHP 实现的搜索引擎及分词方案吧。这一套方案由两个组件组成&#xff0c;一个叫 TNTSearch &#xf…

【0到1的设计之路】从C语言到二进制程序

C程序如何从源代码生成指令序列(二进制可执行文件) 预处理 -> 编译 -> 汇编 -> 链接 -> 执行 预处理 预处理 文本粘贴 #include <stdio.h> #define MSG "Hello \ World!\n" int main() {printf(MSG /* "hi!\n" */); #ifdef __riscvpr…

PE解释器之PE文件结构(二)

接下来的内容是对IMAGE_OPTIONAL_HEADER32中的最后一个成员DataDirectory&#xff0c;虽然他只是一个结构体数组&#xff0c;每个结构体的大小也不过是个字节&#xff0c;但是它却是PE文件中最重要的成员。PE装载器通过查看它才能准确的找到某个函数或某个资源。 一&#xff1…

2种数控棋

目录 数控棋1 数控棋2 数控棋1 棋盘&#xff1a; 初始局面&#xff1a; 规则&#xff1a; 规则&#xff1a;双方轮流走棋&#xff0c;可走横格、竖格、可横竖转弯&#xff0c;不可走斜格。每一步均须按棋所在格的数字走步数&#xff0c;不可多不可少。 先无法走棋的一方为…

如何有效防爬虫?一文讲解反爬虫策略

企业拥抱数字化技术的过程中&#xff0c;网络犯罪分子的“战术”也更难以觉察&#xff0c;并且这些攻击越来越自动化和复杂&#xff0c;也更加难以觉察。在众多攻击手段中&#xff0c;网络爬虫是企业面临的主要安全挑战。恶意爬虫活动可能导致数据滥用、盗窃商业机密等问题&…

ctfshow php特性(web89-web101)

目录 web89 web90 web91 web92 web93 web94 web95 web96 web97 web98 web99 web100 web101 php特性(php基础知识) web89 <?php include("flag.php"); highlight_file(_FILE_);if(isset($_GET[num])){$num$_GET[num];if(preg_match("/[0-9]/&…

ffmpeg 常用命令行详解

概述 ffmpeg 是一个命令行音视频后期处理软件 1. 裁剪命令 参数说明 -i 文件&#xff0c;orgin.mp3 为待处理源文件-ss 裁剪时间&#xff0c;后跟裁剪开始时间&#xff0c;或者开始的秒数-t 裁剪时间output.mp3 为处理结果文件 ffmpeg -i organ.mp3 -ss 00:00:xx -t 120 o…

Oracle 隐式数据类型转换

目录 Oracle类型转换规则&#xff1a; 看如下实验&#xff1a; 1、创建一张表&#xff0c;字段id的类型为number&#xff0c;id字段创建索引&#xff0c;插入一条测试数据 2、我们做如下查询&#xff0c;id的值设置为字符型的1 3、查看执行计划&#xff1a; Oracle类型转换…

MySQL之索引结构

索引概述 索引是帮助MySQL高效获取数据的数据结构&#xff08;有序&#xff09;。 在数据之外&#xff0c;数据库系统还维护着满足特定查找算法的数据结构&#xff0c;这些数据结构以某种方式引用&#xff08;指向&#xff09;数据&#xff0c;这样就可以在这些数据结构上实现…

JMeter实操入门之登录

JMeter实操入门之登录 前言初级-无变量的登录线程组取样器-HTTP请求 进阶-定义变量的登录用户定义的变量获取JSON返回的数据-tokentoken设置全局变量 前言 安装及环境配置教程可移步&#xff1a;JMeter安装与配置环境 本篇文章针对小白进一步的认识及运用JMeter&#xff0c;围绕…