【刷题总结】堆系列问题

一、堆的定义

  • 堆的定义:堆是一种特别的二叉树,具有两个特性
    • 完全二叉树;
    • 每一个节点的值都必须 大于等于或者小于等于 其孩子节点的值。
  • 堆的特性
    • 插入元素:O(logN)
    • 删除元素:O(logN)
    • 获取 堆 中的最大值或最小值:O(1)
  • 堆的分类
    • 最大堆:堆中每一个节点的值 都大于等于 其孩子节点的值。所以最大堆的特性是 堆顶元素(根节点)是堆中的最大值。
    • 最小堆:堆中每一个节点的值 都小于等于 其孩子节点的值。所以最小堆的特性是 堆顶元素(根节点)是堆中的最小值。

二、堆的实现

C++ 中堆采用优先级队列实现,常用操作如下

priority_queue<int> q; 
priority_queue<int, vector<int>, greater<int> > q; //升序队列,小顶堆
priority_queue<int, vector<int>, less<int> > q; //降序队列,大顶堆
q.top();
q.empty();
q.size();
q.push(x);
q.emplace(x) 原地构造一个元素并插入队列
q.pop();

//自定义排序算法
struct cmp{
    bool operator()(const pair<string, int>& l, const pair<string, int>& r){
        if (l.second == r.second)
            return l.first < r.first;
        else
            return l.second > r.second;
    }
};
priority_queue<pair<string, int>, vector<pair<string, int>>, cmp> heap;

三、堆的应用

堆的常见应用有三类:

1、堆排序

2、找前K大的元素

Top K 大元素

解法1:
1. 创建一个「最大堆」;
2. 将所有元素都加到「最大堆」中;
3. 通过边删除边遍历方法,将堆顶元素删除,并将它保存到结果集 T 中;
4. 重复3步骤 K 次,直到取出前K个最大的元素;

解法2:
1.创建一个大小为 K 的「最小堆」;
2.依次将元素添加到「最小堆」中;
3.当「最小堆」的元素个数达到 K 时,将当前元素与堆顶元素进行对比:
4.如果当前元素小于堆顶元素,则放弃当前元素,继续进行下一个元素;
5.如果当前元素大于堆顶元素,则删除堆顶元素,将当前元素加入到「最小堆」中。
6.重复步骤 2 和步骤 3,直到所有元素遍历完毕。

Top K 小元素:与topK大元素解法一致,最大堆换成最小堆即可

3、第K大元素问题

The Kth 大元素

解法1:
1.创建一个「最大堆」;
2.将所有元素都加到「最大堆」中;
3.通过 「边删除边遍历」方法,将堆顶元素删除;
4.重复 3 步骤 K 次,直到获取到第 K 个最大的元素;

解法2:
1.创建一个大小为 K 的「最小堆」;
2.依次将元素添加到「最小堆」中;
3.当「最小堆」的元素个数达到 K 时,将当前元素与堆顶元素进行对比:
  如果当前元素小于堆顶元素,则放弃当前元素,继续进行下一个元素;
  如果当前元素大于堆顶元素,则删除堆顶元素,将当前元素加入到「最小堆」中。
4.重复步骤 2 和步骤 3,直到所有元素遍历完毕。

The Kth 小元素 :topK大元素解法一致,最大堆换成最小堆即可

四、堆系列问题

堆系列问题题目:https://leetcode-cn.com/tag/heap/problemset/

1、找前K大元素问题

  1. 剑指 Offer 40. 最小的k个数(easy):典型前 K 小元素问题。创建大小为K的最大堆存前K小的元素,如果小于堆顶则堆顶出堆将新元素入堆,堆内元素即为前K小元素

  2. 347. 前 K 个高频元素(medium):给出一个整数数组,求出现频率前k高的元素。采用hash记录频次,采用大小为k的存较大元素的最小堆来存放元素,堆内即为较大的k个元素

  3. 373. 查找和最小的K对数字(medium):给出一堆整数对,求和最小的k对数字。计算完后存到hash中,采用大小为k的存较小元素的最大堆来存放和,堆内即为和最小的k对数字

  4. 692. 前K个高频单词(medium):统计出现频率最高的前K个元素,并按字母序排列。采用hash存频次,用大小为k的存较大元素的最小堆来存放频次,堆内即为和最大的k个单词,出堆根据字母序排序

  5. 451. 根据字符出现频率排序(medium):要求根据字符出现频率降序排序。采用hash存频次,用最大堆来存放频次,入堆完再根据出堆顺序排序

  6. 973. 最接近原点的 K 个点(medium):给出一些坐标求最接近原点的K个点。采用大小为K的存较小元素的最大堆来存距离,堆内即为最近的k个点

  7. 857. 雇佣 K 名工人的最低成本(hard):给出工人的工作质量及期望薪资,求雇佣k个工人最少需要多少钱。计算工人的性价比,并放入大小为k的存性价比较高的小顶堆中,堆中即为性价比较高的k个工人

2、第K大元素问题

  1. 703. 数据流中的第 K 大元素(easy):求数据流中第K大的元素。一个大小为K的最小堆存较大的K个数,大于堆顶则入堆,堆顶即为第K大的元素
  2. 215. 数组中的第K个最大元素(medium):在未排序数组中找第K大的元素。采用K大的最小堆存最大的K个数
  3. 378. 有序矩阵中第 K 小的元素(medium):求在二维矩阵中找第K小的数。同样是找第K小的数,只是变为了二维数组,用一个大小为K存较小元素的大顶堆,小于堆顶的元素可以入堆,遍历完后堆顶即为结果
  4. 264. 丑数 II(medium):丑数为只由2/3/5乘的数,求返回第n个丑数。回第n小的丑数,第n小的整数一定在相乘n次内,则求出相乘n次得到的所有丑数,放入大小为n的存较小元素的最大堆中,堆顶即为第n小的丑数
  5. 313. 超级丑数(medium):超级丑数是指所有质因数都是质数列表中的正整数,求第n个超级丑数。此题与上题的区别为相乘的数变为给定的,解法一致,仍未求出相乘n次得到的超级丑数并放入大小为n存较小元素的最大堆中,堆顶即为第n小的超级丑数

3、找中位数问题

  1. 295. 数据流的中位数(hard):不断求数据流中的中位数。看到中位数要意识到中位数只是特殊的第K大的元素,仍为找第K大元素问题,中位数问题采用两个堆,一个大小为n/2的小顶堆存较大元素,一个大小为n/2的大顶堆存较小元素,则堆顶即为中间的数

4、堆排序-动态顺序的问题

  1. 1046. 最后一块石头的重量(easy):给出一堆石头,选出两块石头会粉碎成两块石头的差值。贪心的思想:每次都从最大的石头开始粉碎,且数组会改变。为了实现对改变的数组每次都取到最大值则采用一个最大堆来存

  2. 23. 合并K个升序链表(hard):给出K个链表数组,对所有链表进行合并排序。采用堆排序实现,所有节点入堆,按照出队顺序构造新链表

  3. 239. 滑动窗口最大值(hard):给出整数数组,上面有长度为k的滑动窗口,不断求滑动窗口中的最大值。采用最大堆来不断加入元素,当堆顶元素不在滑动窗口中时出堆,否则取堆顶

  4. 1845. 座位预约管理系统(medium):要求设计一个购票系统,每次取最小的号,支持动态放回。本题要支持动态变化的数组取最小值,动态排序问题采用堆实现,采用小顶堆存数值,每次取堆顶即为最小

  5. 1753. 移除石子的最大得分(medium):给出三堆石子,每次要选两堆拿走一个石子,当有两堆为空时结束,求最多的得分。贪心的思想:每次都从最高的两组里面取,采用大顶堆来实现动态排序,每次取堆顶两个,处理后存回

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值