当前位置: 首页 > news >正文

每日算法刷题Day61:8.11:leetcode 堆11道题,用时2h30min

分享丨【算法题单】常用数据结构(前缀和/栈/队列/堆/字典树/并查集/树状数组/线段树)- 讨论 - 力扣(LeetCode)

一、基础

1.套路

1.priority_queue<int>,跟queue一样,都是容器适配器,默认数组升序,最后一个元素为最大值,堆顶为最大值,故称为大顶堆。
每次取出来的是最大值,即值越大优先级越高。

priority_queue<queue> pq; // 最大堆
int x=pq.top(); // 堆顶
pq.pop(); // 弹出堆顶
pq.push(x); // 插入元素,自动排序
int len=pq.size();
bool tag=pq.empty();

2.vector直接赋值到优先队列
priority_queue<int> pq(nums.begin(),nums.end())
注意:queue不行
3.最小堆写法:

priority_queue<int,vector<int>,greater<int>> pq;
// 数组降序排列,故最后一个元素最小,为小顶堆

4.结构体自定义优先级写法(较难):

struct Node{int x,y;
};// 比较器结构体,不写在Node里面
struct cmp{// 比较器,是(),而不是<bool operator()(const Node &a, const Node &b){return a.x>b.x; // 为true时,前面的是parent,为更大值,故为大顶堆}
}priority_queue<Node,vector<Node>,cmp> pq;

如果理解return a.x>b.x;?
(1)从priority_queue的底层逻辑看,他是在维护堆时判断:
if(comp(parent,child)) then swap(parent,child)
所以当a.x>b.xtrue时,aparent变成child,而bchild变成parent,所以较小的元素成为parent,即较小的元素优先级最高,为小顶堆。
(2)(好记忆)类比堆排序,a.x>b.x降序排序,数组(类比,不是严格降序,但能保证最后一个元素为最小值)的最后一个元素为最小值,而最后一个元素为堆顶,故为小顶堆。

2.题目描述
3.学习经验

1.快速获取最大值/最小值,并进行一些操作,将操作后的值放回去(要用优先队列),然后往复操作。
2.topK问题[[九.堆(优先队列)#8. 703. 数据流中的第K大元素(简单,学习)]],堆中元素数量维护为K个。
topK大,则建立小顶堆,因为堆顶为第K大元素,是前K个元素最小的。
topK小,则建立大顶堆。

1. 1046. 最后一块石头的重量(简单,学习)

1046. 最后一块石头的重量 - 力扣(LeetCode)

思想

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

  • 如果 x == y,那么两块石头都会被完全粉碎;
  • 如果 x != y,那么重量为 x 的石头将会完全粉碎,而重量为 y 的石头新重量为 y-x
    最后,最多只会剩下一块石头。返回此石头的重量。如果没有石头剩下,就返回 0
    2.每次取两块最重的就想到大顶堆
代码
class Solution {
public:int lastStoneWeight(vector<int>& stones) {int n = stones.size();priority_queue<int> pq;for (int i = 0; i < n; ++i) {pq.push(stones[i]);}while (pq.size() > 1) {int x = pq.top();pq.pop();int y = pq.top();pq.pop();if (x > y)pq.push(x - y);}return pq.empty() ? 0 : pq.top();}
};
2. 3264. K次乘运算后的最终数组I(简单)

3264. K 次乘运算后的最终数组 I - 力扣(LeetCode)

思想

1.给你一个整数数组 nums ,一个整数 k  和一个整数 multiplier 。
你需要对 nums 执行 k 次操作,每次操作中:

  • 找到 nums 中的 最小 值 x ,如果存在多个最小值,选择最 前面 的一个。
  • 将 x 替换为 x * multiplier 。
    请你返回执行完 k 次乘运算之后,最终的 nums 数组。
    2.这题每次取最小值能想到小顶堆,但是要更新x并返回原顺序的结果数组,还得知道x的下标,所以要用到结构体。所以要自定义优先队列优先级。
代码
class Solution {
public:struct Node {int id;int x;Node(int _id, int _x) : id(_id), x(_x) {}};struct cmp {bool operator()(const Node& a, const Node& b) {// 小顶堆if (a.x != b.x)return a.x > b.x;return a.id > b.id;}};priority_queue<Node, vector<Node>, cmp> pq;vector<int> getFinalState(vector<int>& nums, int k, int multiplier) {int n = nums.size();for (int i = 0; i < n; ++i) {pq.push(Node(i, nums[i]));}while (k) {Node t = pq.top();pq.pop();int val = nums[t.id] * multiplier, tid = t.id;nums[tid] = val;;pq.push(Node{tid, val});--k;}return nums;}
};
3. 2558. 从数量最多的堆取走礼物(简单)

2558. 从数量最多的堆取走礼物 - 力扣(LeetCode)

思想

1.给你一个整数数组 gifts ,表示各堆礼物的数量。每一秒,你需要执行以下操作:

  • 选择礼物数量最多的那一堆。
  • 如果不止一堆都符合礼物数量最多,从中选择任一堆即可。
  • 将堆中的礼物数量减少到堆中原来礼物数量的平方根,向下取整。
    返回在 k 秒后剩下的礼物数量。
    2.每次获得礼物数量最多的那一堆,想到最大堆。
    放回平方根,但是结果不要求数组顺序,所以比第2题更简单,无需结构体。
代码
class Solution {
public:long long pickGifts(vector<int>& gifts, int k) {int n = gifts.size();long long res = 0;priority_queue<int> pq;for (int i = 0; i < n; ++i) {pq.push(gifts[i]);res += gifts[i];}while (k) {int x = pq.top();pq.pop();int curx = sqrt(x);pq.push(curx);res -= (x - curx);--k;}return res;}
};
4. 2336. 无限集中的最小数字(中等,学习思想)

2336. 无限集中的最小数字 - 力扣(LeetCode)

思想

1.现有一个包含所有正整数的集合 [1, 2, 3, 4, 5, ...] 。
实现 SmallestInfiniteSet 类:

  • SmallestInfiniteSet() 初始化 SmallestInfiniteSet 对象以包含 所有 正整数。
  • int popSmallest() 移除 并返回该无限集中的最小整数。
  • void addBack(int num) 如果正整数 num  存在于无限集中,则将一个 num 添加 到该无限集中。
    2.取最小整数肯定能想到最小堆,但是这题一开始是无限集,最小堆里面有无限个元素,无法实现。
    可以将原无限集划分为[id,+无穷]的连续集合,和离散集合,离散集合的最小值就用最小堆实现,但是还要去重,所以来个集合。
    所以两个操作的逻辑如下:
    (1)popSmallest:
  • 最小堆不为空,则取堆顶,为最小整数。最小堆和集合删去元素。
  • 否则,取连续集合第一个id,然后++id
    (2)addBack():
  • num>=id:不产生任何影响
  • num=id-1,连续集合向左移一个,--id
  • num<id:
    • num在集合中,不产生任何影响,可以与第一种合并
    • num不在集合中,num进入最小堆和集合
代码
class SmallestInfiniteSet {
public:int id;                                            // [id,=无穷]都在无限集中priority_queue<int, vector<int>, greater<int>> pq; // 小顶堆set<int> st;                                       // 去重SmallestInfiniteSet() { id = 1; }int popSmallest() {// 优先取小顶堆int res = 0;if (!pq.empty()) {res = pq.top();pq.pop();st.erase(res);} else { // 否则取无限集res = id;++id;}return res;}void addBack(int num) {// num在无限集中才操作if (num >= id || st.count(num))return;else if (num == id - 1)--id;else {pq.push(num);st.insert(num);}}
};/*** Your SmallestInfiniteSet object will be instantiated and called as such:* SmallestInfiniteSet* obj = new SmallestInfiniteSet();* int param_1 = obj->popSmallest();* obj->addBack(num);*/
5. 2530. 执行K次操作后的最大分数(中等)

2530. 执行 K 次操作后的最大分数 - 力扣(LeetCode)

思想

1.给你一个下标从 0 开始的整数数组 nums 和一个整数 k 。你的 起始分数 为 0 。
在一步 操作 中:

  1. 选出一个满足 0 <= i < nums.length 的下标 i ,
  2. 将你的 分数 增加 nums[i] ,并且
  3. 将 nums[i] 替换为 ceil(nums[i] / 3) 。
    返回在 恰好 执行 k 次操作后,你可能获得的最大分数。
    向上取整函数 ceil(val) 的结果是大于或等于 val 的最小整数。
    2.取最大分数就是大顶堆,然后先上取整利用套路加上2。
代码
class Solution {
public:long long maxKelements(vector<int>& nums, int k) {int n = nums.size();long long res = 0;priority_queue<int> pq;for (int i = 0; i < n; ++i)pq.push(nums[i]);while (k) {int x = pq.top();pq.pop();res += x;pq.push((x + 3 - 1) / 3);--k;}return res;}
};
6. 3066. 超过阈值的最少操作数II(中等)

[3066. 超过阈值的最少操作数 II - 力扣(LeetCode)](https://leetcode.cn/problems/minimum-operations-to-exceed-threshold-value-ii/description/

思想

1.给你一个下标从 0 开始的整数数组 nums 和一个整数 k 。
你可以对 nums 执行一些操作,在一次操作中,你可以:

  • 选择 nums 中 最小 的两个整数 x 和 y 。
  • 将 x 和 y 从 nums 中删除。
  • 将 min(x, y) * 2 + max(x, y) 添加到数组中的任意位置。
    **注意,**只有当 nums 至少 包含两个元素时,你才可以执行以上操作。
    你需要使数组中的所有元素都 大于或等于 k ,请你返回需要的 最少 操作次数。
    2.该开long long还是要开,不要猜测
代码
class Solution {
public:typedef long long ll;int minOperations(vector<int>& nums, int k) {int n = nums.size();priority_queue<ll, vector<ll>, greater<ll>> pq(nums.begin(),nums.end());ll res = 0;while (pq.top() < k) {ll x = pq.top();pq.pop();ll y = pq.top();pq.pop();pq.push(1LL * x * 2 + y);++res;}return res;}
};
7. 1962. 移除石子使总数最小(中等)

1962. 移除石子使总数最小 - 力扣(LeetCode)

思想

1.给你一个整数数组 piles ,数组 下标从 0 开始 ,其中 piles[i] 表示第 i 堆石子中的石子数量。另给你一个整数 k ,请你执行下述操作 恰好 k 次:

  • 选出任一石子堆 piles[i] ,并从中 移除 floor(piles[i] / 2) 颗石子。
    **注意:**你可以对 同一堆 石子多次执行此操作。
    返回执行 k 次操作后,剩下石子的 最小 总数。
    floor(x) 为 小于 或 等于 x 的 最大 整数。(即,对 x 向下取整)。
    2.跟[[九.堆(优先队列)#3. 2558. 从数量最多的堆取走礼物(简单)]]一模一样
代码
class Solution {
public:int minStoneSum(vector<int>& piles, int k) {int n = piles.size();int res = 0;priority_queue<int> pq(piles.begin(), piles.end());for (int i = 0; i < n; ++i)res += piles[i];while (k) {int x = pq.top();pq.pop();int dif = x / 2;pq.push(x - dif);res -= dif;--k;}return res;}
};
8. 703. 数据流中的第K大元素(简单,重点学习)

703. 数据流中的第 K 大元素 - 力扣(LeetCode)

思想

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

  • KthLargest(int k, int[] nums) 使用整数 k 和整数流 nums 初始化对象。
  • int add(int val) 将 val 插入数据流 nums 后,返回当前数据流中第 k 大的元素。
    2.此题是经典题,要求第K大元素。可以利用小根堆维护前K大个元素(用小根堆是因为堆顶就是第K大元素),然后元素进来插入小根堆,内部排序不用我们管,如果堆元素超过K个,则弹出堆顶(比第K大元素小)。
    有点像单调队列的右侧进入元素维护单调性,但是此题只维护堆中为前K大元素,堆顶是第K大元素,前K大元素的顺序无法保证
代码
class KthLargest {
public:int len;priority_queue<int, vector<int>, greater<int>> pq;KthLargest(int k, vector<int>& nums) {len = k;int n = nums.size();for (int i = 0; i < n; ++i) {pq.push(nums[i]);if (pq.size() > len)pq.pop();}}int add(int val) {pq.push(val);if (pq.size() > len)pq.pop();return pq.top();}
};/*** Your KthLargest object will be instantiated and called as such:* KthLargest* obj = new KthLargest(k, nums);* int param_1 = obj->add(val);*/
9. 3275. 第K近障碍物查询(中等)

3275. 第 K 近障碍物查询 - 力扣(LeetCode)

思想

1.有一个无限大的二维平面。
给你一个正整数 k ,同时给你一个二维数组 queries ,包含一系列查询:

  • queries[i] = [x, y] :在平面上坐标 (x, y) 处建一个障碍物,数据保证之前的查询 不会 在这个坐标处建立任何障碍物。
    每次查询后,你需要找到离原点第 k  障碍物到原点的 距离 。
    请你返回一个整数数组 results ,其中 results[i] 表示建立第 i 个障碍物以后,离原地第 k 近障碍物距离原点的距离。如果少于 k 个障碍物,results[i] == -1 。
    注意,一开始 没有 任何障碍物。
    坐标在 (x, y) 处的点距离原点的距离定义为 |x| + |y| 。
    2,求topK小距离,所以为大顶堆
代码
class Solution {
public:typedef long long ll;vector<int> resultsArray(vector<vector<int>>& queries, int k) {int n = queries.size();vector<int> res(n, -1);priority_queue<ll> pq;for (int i = 0; i < n; ++i) {int x = queries[i][0], y = queries[i][1];ll dis = abs(x) + abs(y);pq.push(dis);if (pq.size() > k)pq.pop();if (pq.size() < k)res[i] = -1;elseres[i] = pq.top();}return res;}
};
10. 1845. 座位预约管理系统(中等)

1845. 座位预约管理系统 - 力扣(LeetCode)

思想

1.请你设计一个管理 n 个座位预约的系统,座位编号从 1 到 n 。
请你实现 SeatManager 类:

  • SeatManager(int n) 初始化一个 SeatManager 对象,它管理从 1 到 n 编号的 n 个座位。所有座位初始都是可预约的。
  • int reserve() 返回可以预约座位的 最小编号 ,此座位变为不可预约。
  • void unreserve(int seatNumber) 将给定编号 seatNumber 对应的座位变成可以预约。
    2.- 每一次对 reserve 的调用,题目保证至少存在一个可以预约的座位。
  • 每一次对 unreserve 的调用,题目保证 seatNumber 在调用函数前都是被预约状态。
    所以一个优先队列就行。但是平时最好来个哈希表去重。
代码
class SeatManager {
public:priority_queue<int, vector<int>, greater<int>> pq;SeatManager(int n) {for (int i = 1; i <= n; ++i) {pq.push(i);}}int reserve() {int x = pq.top();pq.pop();return x;}void unreserve(int seatNumber) { pq.push(seatNumber); }
};/*** Your SeatManager object will be instantiated and called as such:* SeatManager* obj = new SeatManager(n);* int param_1 = obj->reserve();* obj->unreserve(seatNumber);*/
11. 2208. 将数组和减半的最少操作次数(中等,学习避免浮点数方法,但是不常用)

2208. 将数组和减半的最少操作次数 - 力扣(LeetCode)

思想

1.给你一个正整数数组 nums 。每一次操作中,你可以从 nums 中选择 任意 一个数并将它减小到 恰好 一半。(注意,在后续操作中你可以对减半过的数继续执行操作)
请你返回将 nums 数组和 至少 减少一半的 最少 操作数。
2,浮点数会产生误差,因为是每次减半,所以可以把数乘以2的多少次幂进行放大,保证到结束时他还是整数,可以证明次数不会超过20。(证明先放一下,学习这个思想,到时候写个20能过很多)

代码
class Solution {
public:int halveArray(vector<int>& nums) {int n = nums.size();double sum = 0;priority_queue<double> pq;for (int i = 0; i < n; ++i) {sum += 1.0 * nums[i];pq.push(1.0 * nums[i]);}double k = sum / 2;int res = 0;while (sum > k) {double x = pq.top();pq.pop();sum -= x / 2;pq.push(x / 2);++res;}return res;}
};

不用浮点数

class Solution {
public:typedef long long ll;int halveArray(vector<int>& nums) {int n = nums.size();ll sum = 0;priority_queue<ll> pq;for (int i = 0; i < n; ++i) {ll tmp = 1LL * nums[i]<< 20; // 先把nums[i]转成long long才能左移,不然会溢出sum += tmp;pq.push(tmp);}ll k = sum / 2;int res = 0;while (sum > k) {ll x = pq.top();pq.pop();sum -= x / 2;pq.push(x / 2);++res;}return res;}
};
http://www.dtcms.com/a/326644.html

相关文章:

  • 【功能测试】软件集成测试思路策略与经验总结
  • HTML应用指南:利用GET请求获取全国vivo体验店门店位置信息
  • 字节后端面经
  • 内网依赖管理新思路:Nexus与CPolar的协同实践
  • Linux-FTP服务器搭建
  • 【图像算法 - 12】OpenCV-Python 入门指南:图像视频处理与可视化(代码实战 + 视频教程 + 人脸识别项目讲解)
  • DHCP服务配置与管理实战指南
  • CRMEB多商户系统(Java)v2.0更新预告:区域管理+预约商品,激活本地商业新活力!
  • NTC热敏电阻、压敏电阻和保险丝工作原理
  • FFmpeg - 基本 API大全(视频编解码相关的)
  • python每日一题练习 两个数组的交集 非常简单
  • GCN: 图卷积网络,概念以及代码实现
  • 【LeetCode刷题集】--排序(三)
  • Protocol Buffers (protobuf) API 接口完全指南
  • maven项目打包成sdk后在别的项目使用
  • 从0开始的中后台管理系统-5(部门管理以及菜单管理页面功能实现)
  • 【科研绘图系列】R语言绘制散点图折线图误差棒组合图
  • 指派问题-匈牙利算法
  • 2025牛客多校第八场 根号-2进制 个人题解
  • HTTPS应用层协议-CA签名与证书
  • Vue 3 快速入门 第六章
  • MaixPy简介
  • Projects
  • 进程管理是什么
  • DeepSeek生成的高精度大数计算器
  • 自制网页并爬取,处理异常值(第十九节课内容总结)
  • .NET/C# webapi框架下给swagger的api文档中显示注释(可下载源码)
  • MP3Tag 软件功能简介
  • (二)vscode搭建espidf环境,配置wsl2
  • 第16届蓝桥杯Python青少组中/高级组选拔赛(STEMA)2025年4月真题