春招百题--堆

一、堆的定义

二、堆(优先队列)

堆通常用于实现优先队列(priority_queue),大顶堆相当于元素按从大到小的顺序出队的优先队列。从使用角度来看,我们可以将“优先队列”和“堆”看作等价的数据结构。

堆的常见用法:

/* 初始化堆 */
// 初始化小顶堆
priority_queue<int, vector<int>, greater<int>> minHeap;
// 初始化大顶堆
priority_queue<int, vector<int>, less<int>> maxHeap;

/* 元素入堆 */
maxHeap.push(1);
maxHeap.push(3);
maxHeap.push(2);
maxHeap.push(5);
maxHeap.push(4);

/* 获取堆顶元素 */
int peek = maxHeap.top(); // 5

/* 堆顶元素出堆 */
// 出堆元素会形成一个从大到小的序列
maxHeap.pop(); // 5
maxHeap.pop(); // 4
maxHeap.pop(); // 3
maxHeap.pop(); // 2
maxHeap.pop(); // 1

/* 获取堆大小 */
int size = maxHeap.size();

/* 判断堆是否为空 */
bool isEmpty = maxHeap.empty();

/* 输入列表并建堆 */
vector<int> input{1, 3, 2, 5, 4};
priority_queue<int, vector<int>, greater<int>> minHeap(input.begin(), input.end());

三、堆的常见应用

  • 优先队列:堆通常作为实现优先队列的首选数据结构,其入队和出队操作的时间复杂度均为 0(log⁡n) ,而建队操作为 0(n),这些操作都非常高效。
  • 堆排序:给定一组数据,我们可以用它们建立一个堆,然后不断地执行元素出堆操作,从而得到有序数据。
  • 获取最大的 K 个元素:这是一个经典的算法问题,同时也是一种典型应用,例如选择热度前 10 的新闻作为微博热搜,选取销量前 10 的商品等。

四、具体思路top-k问题:

  1. 初始化一个小顶堆,其堆顶元素最小。
  2. 先将数组的前 k 个元素依次入堆。
  3. 从第 k+1 个元素开始,若当前元素大于堆顶元素,则将堆顶元素出堆,并将当前元素入堆。
  4. 遍历完成后,堆中保存的就是最大的 k个元素。
/* 基于堆查找数组中最大的 k 个元素 */
priority_queue<int, vector<int>, greater<int>> topKHeap(vector<int> &nums, int k) {
    // 初始化小顶堆
    priority_queue<int, vector<int>, greater<int>> heap;
    // 将数组的前 k 个元素入堆
    for (int i = 0; i < k; i++) {
        heap.push(nums[i]);
    }
    // 从第 k+1 个元素开始,保持堆的长度为 k
    for (int i = k; i < nums.size(); i++) {
        // 若当前元素大于堆顶元素,则将堆顶元素出堆、当前元素入堆
        if (nums[i] > heap.top()) {
            heap.pop();
            heap.push(nums[i]);
        }
    }
    return heap;
}

总共执行了 n 轮入堆和出堆,堆的最大长度为 k ,因此时间复杂度为 0(nlogk)。该方法的效率很高,当 k 较小时,时间复杂度趋向0(n) ;当k 较大时,时间复杂度不会超过 0(nlog⁡n) 。

另外,该方法适用于动态数据流的使用场景。在不断加入数据时,我们可以持续维护堆内的元素,从而实现最大的 k个元素的动态更新

五、习题:

215. 数组中的第K个最大元素

class Solution {
public:
    int findKthLargest(vector<int>& nums, int k) {
        //建大顶堆
        priority_queue<int,vector<int>,greater<int>>heap;
        int n=nums.size();
        for(int i=0;i<k;i++){
            heap.push(nums[i]);
        }
        for(int i=k;i<n;i++){
            if(nums[i]>heap.top()){
                heap.pop();
                heap.push(nums[i]);
            }   
        }
        return heap.top();
    }
};

264. 丑数 II

给你一个整数 n ,请你找出并返回第 n 个 丑数 。

丑数 就是质因子只包含 23 和 5 的正整数。

思路:如果x是丑数,则2x、3x、5x也是丑数。所以我们不妨使用一个优先队列,存储这些丑数。

每次取出堆顶元素 x,则 x 是堆中最小的丑数,由于 2x,3x,5x 也是丑数,因此将 2x,3x,5x加入堆。

上述做法会导致堆中出现重复元素的情况。为了避免重复元素,可以使用哈希集合去重,避免相同元素多次加入堆。

哈希去重:

unordered_set<long> seen;//建立哈希表
//seen.count(x)//对x元素计数。

if(!seen.count(x))//x此时一个都没有
seen.insert(x);//插入到哈希表中,此时数量就为一了
class Solution {
public:
    int nthUglyNumber(int n) {
        //哈希表去重
        unordered_set<long> seen;
        
        vector<int>factors={2,3,5};
        priority_queue<long,vector<long>,greater<long>>heap;
        seen.insert(1L);
        heap.push(1L);
        int ugly=0;
        for(int i=0;i<n;i++){
            long curr=heap.top();
            heap.pop();
            ugly=(int)curr;

            for(int factor:factors){
                long next=curr*factor;
                if(!seen.count(next)){
                    seen.insert(next);
                    heap.push(next);
                }
            }
        }
        return ugly;
    }
};

另外一个方法:. - 力扣(LeetCode)

  • 5
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值