【基础算法总结】优先级队列

news2024/9/24 13:20:28

优先级队列

  • 1.最后一块石头的重量
  • 2.数据流中的第 K 大元素
  • 4.前K个高频单词
  • 4.数据流的中位数

在这里插入图片描述

点赞👍👍收藏🌟🌟关注💖💖
你的支持是对我最大的鼓励,我们一起努力吧!😃😃

1.最后一块石头的重量

题目链接:1046. 最后一块石头的重量

题目分析:

在这里插入图片描述

每一回合,从中选出两块 最重的 石头,x == y,那么两块石头都会被完全粉碎;如果 x != y,那么重量较小的 x 石头将会完全粉碎,而重量较大的 y 的石头新重量为 y-x。最多只会剩下一块石头。返回此石头的重量。如果没有石头剩下,就返回 0。

在这里插入图片描述

算法原理:

每次挑选的是先挑一堆数中最大的那个数,然后再挑一个剩下数中最大的数。这不正好符合大根堆的数据结构吗。

解法:用堆来模拟这个过程

先拿数组的数创建一个大根堆,每次从堆里面选择最大和次大两个数,如果相等就是0不用加入到大根堆里,如果不相等用最大的数减去次大的数,把结果加入到堆里面。如果最后堆里还剩下一个数,返回这个数。如果堆为空说明石头全都粉碎了返回0即可。

class Solution {
public:
    int lastStoneWeight(vector<int>& stones) {

        //1.拿元素创建一个大根堆
        priority_queue<int> heap(stones.begin(),stones.end());

        //2.模拟这个过程
        while(heap.size() >= 2)
        {
            int s1 = heap.top();
            heap.pop();

            int s2 = heap.top();
            heap.pop();

            if(s1 != s2) heap.push(s1 - s2);
        }
        return heap.size() ? heap.top() : 0;
    }
};

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

题目链接:703. 数据流中的第 K 大元素

题目分析:

在这里插入图片描述
设计一个找到数据流中第 k 大元素的类(class)。注意是排序后的第 k 大元素,不是第 k 个不同的元素。

在这里插入图片描述
int add(int val) 将 val 插入数据流 nums 后,返回当前数据流中第 k 大的元素。

在这里插入图片描述

算法原理:

这道题其实考察的是一个非常经典的问题, TopK问题。
关于TopK问题有两种解题思路,一种是堆,一种是前面刚学的快速选择算法。快速选择排序虽然快,但是对于海量的数据内存根本放不下。所以在海量数据情况最优的还是堆。

在这里插入图片描述

解法:用 堆 来解决

  1. 创建一个大小为 k 的堆(大根堆 or 小根堆)
  2. 循环
    1.依次进堆
    2.判断堆的大小是否超过 K

在堆的实现,画图和代码分析建堆,堆排序,时间复杂度以及TOP-K问题,对于求第K个最大元素,我们也是将前K个数建个小堆,然后将剩下的N-K个元素和堆顶元素做比较,如果大于堆顶元素,就先把栈顶元素pop掉,也就是把堆中最小元素删除,然后将它放进去。这样一直循环,直到所有元素都比较完成。因为是一直把堆中最小的pop掉,那堆的元素就是N个元素中最大的,而堆顶就是第K个最大的元素,别忘记我们这是一个小堆!

这里我们也是建小堆,依次进堆,当堆大小超过K个,pop堆顶元素,把堆中最小元素pop掉,并且使堆保持K个大小。每次都是堆中最小元素去掉。那最后堆中剩下的就是前K个最大元素,而堆顶就是第K个最大元素。

考虑一下下面两个问题

  1. 用大根堆还是小根堆
  2. 为什么要用大根堆(小根堆)
class KthLargest {
public:
    
    priority_queue<int,vector<int>,greater<int>> heap;
    int _k;

    KthLargest(int k, vector<int>& nums) {
        _k = k;
        for(auto& 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();

    }
};

4.前K个高频单词

题目链接:692. 前K个高频单词

题目分析:

在这里插入图片描述

这是一个TopK的问题,注意,返回的答案应该按单词出现频率由高到低排序。如果不同的单词有相同出现频率, 按字典顺序(由低向高) 排序。

算法原理:

解法:利用 “堆” 来解决 TopK 问题

  1. 预处理一下原始的字符串数组: 用一个哈希表,统计一下每一个单词出现的频次。

  2. 创建一个大小为 k 的堆,类提供的比较函数满足不了要求,我们要自己定义一个!(返回的答案应该按单词出现频率由高到低排序。如果不同的单词有相同出现频率, 按字典顺序(由低向高) 排序。)如果比较频次创建一个小根堆,如果比较字典序(频次相同的时候)创建一个大根堆。所以说创建堆写比较函数的时候必须要考虑这两点,当频次相同的时候字典序按照大根堆方式比较,当频次不同的时候按照小根堆方式比较。

  3. 循环
    1.依次进堆
    2.判断堆的大小是否超过 K

  4. 提取结果

因为求前K大,所以建的是一个小根堆,然后提取堆顶元素在pop是一个升序的。有两种方式拿最终答案,要么逆序一下取前K个,要么从后往前取K个。

class Solution {
public:

    struct Compare
    {
        bool operator()(const pair<string,int>& l,const pair<string,int>& r)
        {
            //频次不同按照小根堆比较, 频次相同字典序按照大根堆方式比较
            return l.second > r.second || (l.second == r.second && l.first < r.first);
        }
    };

    vector<string> topKFrequent(vector<string>& words, int k) {

        vector<string> ret;
        // 1.统计一下每一个单词的频次
        map<string,int> count;
        for(auto& str : words)
        {
            count[str]++;
        }

        // 2.创建一个大小为 k 的小堆
        priority_queue<pair<string,int>,vector<pair<string,int>>,Compare> heap;


        // 3.TopK 的主逻辑
        for(auto& m : count)
        {
            heap.push(m);
            if(heap.size() > k) heap.pop();
        }

        // 4.提取结果
        vector<string> tmp;
        while(!heap.empty())
        {
            tmp.push_back(heap.top().first);
            heap.pop();
        }

        reverse(tmp.begin(),tmp.end());
        for(int i = 0; i < k; ++i)
            ret.push_back(tmp[i]);

        return ret;
        
    }
};

4.数据流的中位数

题目链接:295. 数据流的中位数

题目分析:

在这里插入图片描述

给一个数据流,让返回每次当前已经加入到数组的数据流的中位数。中位数是有序整数列表中的中间值。如果列表的大小是偶数,则没有中间值,中位数是两个中间值的平均值。表的大小是奇数,直接返回中间的数。

在这里插入图片描述

算法原理:

解法一:直接sort

每次从数据流中来一个数插入数组中之后,都对当前数组进行sort,然后通过下标的方式找到中间数。

在这里插入图片描述

每次add加入一个数都要sort,时间复杂度是O(nlogn)。总体时间复杂度是非常恐怖的。因为是下标访问,find 时间复杂度是 O(1)

解法二:插入排序的思想

[0,end] 有序,插入 end + 1,使 [0, end + 1]有序。这道题正好就是这样的思想。

在这里插入图片描述
add函数,每次插入一个数的时候都需要从后往前扫描找一个插入的位置,因此时间复杂度是O(n),find 也是通过下标去找 时间复杂度是O(1)

解法三:大小堆来维护数据流的中位数

此时有一个数轴已经按照从小到大的排好序了,这个时候想找中间数的时候。把这些数的前半部分放到一个大根堆里面,后半部分放到小根堆里面。此时找中间值也很快,前面较小的数放到大根堆里面,堆顶元素是数轴中这些较小元素种最右边的值。后面较大的数放到小根堆里面,堆顶元素是数轴中这些较大元素最左边。此时我们仅需根据数组中的元素的个数就可以求出中位数是多少了。如果数组是偶数个大根堆和小根堆正好把数轴平分,然后大堆堆顶元素和小堆堆顶元素相加/2就是这个数组的中位数。如果数组是奇数个。我们就先人为规定一下,数轴左边元素是m个,右边是n个,要么 m == n,要么 m > n (n = n + 1)。如果 m == n 正好平均分。如果数轴个数是奇数个,人为规定左边大根堆多方一个元素,m > n(n = n + 1),此时中位数就是左边大根堆的堆顶元素。

在这里插入图片描述
向这样用大根堆存左边较小部分,小根堆存右边较大部分。find 时间复杂度也是O(1),而add快了很多,因为我们是从堆存这些元素的,插入和删除每次调整堆仅需O(logn)

细节问题:

add如何实现:
假设现在有两个堆了。一个大根堆left,一个小根堆right。left元素个数m个,right元素个数n个,left堆顶元素x,right堆定元素y。如果此时来了一个数num,num要么放在left里,要么放在right里。但是放好之后可能会破坏之前的规则:

  1. m == n
  2. m > n —> m == n + 1

我们必须维护上面的规则,才能正确找到数组中位数。
接下来分情况讨论:

  1. m == n

num要么插入left,要么插入right。

如果num要进入left,那么num <= x,但是别忘记 m == n 有可能两个堆全为空,num也是直接进入left。此时 m 比 n 多了一个。没关系直接进就行。

如果num进入right,那条件是 num > x。此时就有问题了。n > m了,而 n > m是不被允许的,所以我们要把右边堆调整一下,就是拿right堆顶元素放到left里。因为right是一个小根堆,堆顶就是最小值。拿到left,还是能保证left堆里元素是较小的,right堆里元素是较大的。拿right堆顶元素放到left里正好满足 m == n + 1。这里有一个细节问题,必须num先进right堆,然后再拿right堆定元素放到left,因为 x < num < y,如果直接把y拿过去了,就破坏了left都是较小元素。right都是较大元素。

在这里插入图片描述

  1. m > n —> m == n + 1

如果num进入left,那么num <= x , 但是此时不满足 m == n + 1,因此 进栈后将栈顶元素给right。

如果num进入right,那么num > x , m == n了,直接进就行了

在这里插入图片描述

class MedianFinder {
public:
    MedianFinder() {
    }
    
    void addNum(int num) {
        // 分类讨论即可


        int m = left.size(), n = right.size();
        if(m == n) // 左右两个堆的元素个数相同
        {
            if(m == 0 || num <= left.top())
                left.push(num);
            else
            {
                right.push(num);
                left.push(right.top());
                right.pop();
            }
        
        }
        else //左边堆的元素个数比右边堆元素个数多 1
        {
            if(num <= left.top())
            {
                left.push(num);
                right.push(left.top());
                left.pop();
            }
            else
                right.push(num);

        }

    }
    
    double findMedian() {
        int m = left.size(), n = right.size();
        
        if(m == n) return (left.top() + right.top()) / 2.0;
        else return left.top();
    }

private: 
    priority_queue<int> left;
    priority_queue<int,vector<int>,greater<int>> right;
};

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

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

相关文章

Unity UGUI 之 Mask

本文仅作学习笔记与交流&#xff0c;不作任何商业用途 本文包括但不限于unity官方手册&#xff0c;唐老狮&#xff0c;麦扣教程知识&#xff0c;引用会标记&#xff0c;如有不足还请斧正 本文在发布时间选用unity 2022.3.8稳定版本&#xff0c;请注意分别 1.什么是遮罩 遮罩是一…

docker搭建python3的私有源--devpi

一、部署 # docker run -d --name devpi-lib -p 7104:7104 --env DEVPISERVER_HOST0.0.0.0 --env DEVPISERVER_PORT7104 --env DEVPISERVER_ROOT_PASSWORDpassword --env DEVPISERVER_USERlowinli --env DEVPISERVER_PASSWORDpassword --env DEVPISERVER_MIRROR_INDEXpypi --…

赋能未来教育,3DCAT助力深圳鹏程技师学院打造5G+XR实训室

随着国家对教育行业的重视&#xff0c;实训室建设已成为推动教育现代化的关键。《教育信息化2.0行动计划》、《职业教育示范性虚拟仿真实训基地建设指南》等政策文件&#xff0c;明确指出了加强虚拟仿真实训教学环境建设的重要性。 在这一大背景下&#xff0c;教育行业对于实训…

Open3D 计算点云的占地面积

目录 一、概述 1.1实现步骤 1.2应用 二、代码实现 1.1关键函数 1.2完整代码 三、实现效果 3.1原始点云 3.2数据显示 前期试读&#xff0c;后续会将博客加入下列链接的专栏&#xff0c;欢迎订阅 Open3D与点云深度学习的应用_白葵新的博客-CSDN博客 一、概述 该博客在…

AI漫画定格新闻大事件,比电影还燃!吸粉超快!AI绘画变现新方法你还不知道吗?!

大家好&#xff0c;我是画画的小强 身处这个信息如潮水般涌来的时代&#xff0c;我们仿佛被连绵不绝的新闻浪潮所淹没。然而&#xff0c;真正能触动我们内心深处&#xff0c;让我们产生强烈共鸣的&#xff0c;往往是那些饱含情感、直击心灵的新闻画面。 在追求效率与速度的今…

草图也能秒变完整画稿?三星 Galaxy Z Fold6 、Flip6硬件升级

在科技的不断进步中&#xff0c;智能手机行业的竞争愈发激烈&#xff0c;各大厂商纷纷推出创新产品以吸引消费者。 最近&#xff0c;三星在 Galaxy Unpacked 发布会上就带来了 Galaxy Z Fold6 和 Flip6 两款手机新品&#xff0c;这两款设备不仅在硬件上有所突破&#xff0c;更…

SpringBoot中使用监听器

1.定义一个事件 /*** 定义事件* author hrui* date 2024/7/25 12:46*/ public class CustomEvent extends ApplicationEvent {private String message;public CustomEvent(Object source, String message) {super(source);this.message message;}public String getMessage() …

MES系统具体有哪些功能?如何更高效的利用MES系统

MES系统&#xff08;Manufacturing Execution System&#xff0c;制造执行系统&#xff09;是现代制造业中非常重要的一个信息化管理系统&#xff0c;它在生产过程中起到了关键的作用。MES系统的具体功能可以归纳如下&#xff1a; 万界星空科技MES系统的具体功能 生产计划管理…

基于MediaPipe的手部特征点识别

基于MediaPipe的手部特征点识别 MediaPipe简介 MediaPipe Solutions 提供了一套库和工具&#xff0c;可以在安卓或者windows应用中快速应用人工智能 (AI) 和机器学习 (ML) 技术。 MediaPipe 手部地标任务可检测图片中手部的特征点。识别效果如下 环境配置 python -m pip ins…

【数据结构】手把手教你单链表(c语言)(附源码)

&#x1f31f;&#x1f31f;作者主页&#xff1a;ephemerals__ &#x1f31f;&#x1f31f;所属专栏&#xff1a;数据结构 目录 前言 1.单链表的概念与结构 2.单链表的结构定义 3.单链表的实现 3.1 单链表的方法声明 3.2 单链表方法实现 3.2.1 打印链表 3.2.2 创建新…

一文详解 JuiceFS 读性能:预读、预取、缓存、FUSE 和对象存储

在高性能计算场景中&#xff0c;往往采用全闪存架构和内核态并行文件系统&#xff0c;以满足性能要求。随着数据规模的增加和分布式系统集群规模的增加&#xff0c;全闪存的高成本和内核客户端的运维复杂性成为主要挑战。 JuiceFS&#xff0c;是一款全用户态的云原生分布式文件…

[240726] Mistral AI 发布新一代旗舰模型 | Node.js 合并 TypeScript 文件执行提案

目录 Mistral AI 发布新一代旗舰模型&#xff1a;Mistral Large 2Node.js 合并 TypeScript 文件执行提案&#xff1a;--experimental-strip-types Mistral AI 发布新一代旗舰模型&#xff1a;Mistral Large 2 Mistral AI 宣布推出新一代旗舰模型 Mistral Large 2&#xff0c;该…

ubuntu20.04 安装vscode 并且配置环境

目录 1.下载 2.安装 3.装插件&#xff0c;配置环境 1.下载 打开浏览器&#xff0c;进入vscode主页 Visual Studio Code - Code Editing. Redefined 2.安装 双击安装包 安装 安装完成后&#xff0c;我们打开vscode 3.装插件&#xff0c;配置环境 打开后我们 ctrlj 打开终…

操作系统的底层管理

一、冯诺依曼体系结构 我们常见的计算机&#xff0c;如笔记本和我们不常见的计算机&#xff0c;如服务器大多都遵循冯诺依曼体系结构。 根据冯诺依曼结构 &#xff0c;我们可以了解到计算机是由一个个硬件组成&#xff0c;共分为三部分&#xff1a;输入、内存/CPU和输出。 如果…

【odoo17 | Owl】前端js钩子调用列表选择视图

概要 在我们选择多对一或者多对多字段的时候&#xff0c;经常看到可以弹出列表弹窗让人一目了然的效果&#xff0c;效果如下&#xff1a; 那么&#xff0c;这种效果是odoo本身封装好的组件&#xff0c;我们在平时的前端界面开发的时候&#xff0c;既不是后端视图的情况下&#…

title可修改点击展示input

<spanv-if"isEdit"click.stop"headerClick(activityForm.activityName)"><span>課程名稱{{ activityForm.activityName}}</span><el-buttonlinktype"primary"style"color: #1d2129"click"editCourseName&qu…

STM32存储左右互搏 QSPI总线读写64 Mbit容量SRAM VTI7064

STM32存储左右互搏 QSPI总线读写64 Mbit容量SRAM VTI7064 QSPI&#xff08;Quad-SPI&#xff09;设备有两种常见操作模式&#xff0c;一种QSPI设备上电后直接进入QSPI模式&#xff0c;操作时命令&#xff0c;地址和数据都是多线传输。另一种QSPI设备上电后进入常规SPI操作模式…

谷粒商城实战笔记-踩坑-跨域问题

一&#xff0c;When allowCredentials is true, allowedOrigins cannot contain the special value “*” since that cannot be set on the “Access-Control-Allow-Origin” response header. To allow credentials to a set of origins, list them explicitly or consider u…

shell-awk文本处理工具

1、awk概述 AWK 是一种处理文本文件的语言&#xff0c;是一个强大的文本分析工具。 它是专门为文本处理设计的编程语言&#xff0c;也是行处理软件&#xff0c;通常用于扫描、过滤、统计汇总工作 数据可以来自标准输入也可以是管道或文件 在 linux 上常用的是 gawk,awk …

PyTorch+AlexNet代码实训

参考文章&#xff1a;https://blog.csdn.net/red_stone1/article/details/122974771 数据集&#xff1a; 打标签&#xff1a; import os# os.path.join: 每个参数都是一个路径段&#xff0c;将它们连接起来形成有效的路径名。 train_txt_path os.path.join("data"…