LeetCode 热题 100 | 堆(二)

目录

1  什么是优先队列

1.1  优先队列与堆的关系

1.2  如何定义优先队列

1.3  如何使用优先队列

1.4  如何设置排序规则

2  347. 前 K 个高频元素

2.1  第 2 步的具体实现

2.2  举例说明

2.3  完整代码

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


菜鸟做题,语言是 C++

1  什么是优先队列

1.1  优先队列与堆的关系

优先队列:它是一种抽象数据类型,可以看作是一个队列或栈的变体,元素按照优先级的高低排列。在 C++ 中,优先队列通常使用堆来实现。

个人理解,优先队列是对堆的底层原理的封装,谁不用谁是傻子 (bushi)

1.2  如何定义优先队列

参考博客:c++ 优先队列(priority_queue)用法详解

定义如下:

priority_queue<Type, Container, Functional>

① Type 是指数据类型,即你要装什么类型的数据。

② Container 是指容器类型,即你用什么样的容器装数据。

Container 必须是用数组实现的容器,比如:vector、deque等等,不能用 list 。STL 里面默认用的是 vector 。

③ Functional 是指比较的方式,即你希望数据之间以什么样的规则来排序。

只有当数据类型比较复杂的时候才需要设置 Functional,比如你的数据是个键值对。针对基本的数据类型,不需要设置 Functional,默认是大根堆(即谁大谁排前面)。

举例说明

假设我要让优先队列装 int 型的数据,那么就有:

priority_queue<int, vector<int>> q;

如果我不想使用默认的大根堆排序方法,则有:

priority_queue<int, vector<int>, decltype(& cmp)> q(cmp);

其中 cmp 是你自定义的排序函数,返回值是 bool 类型。

如果我想让优先队列装键值对类型的数据,则有:

priority_queue<pair<int, int>, vector<pair<int, int>>, decltype(& cmp)> q(cmp);

不过就是把数据类型换了,基本结构都是一样的。

1.3  如何使用优先队列
  • q.push(data) 插入元素
  • q.pop() 弹出队首元素
  • q.top() 获取队首元素
  • q.size() 获取队列大小
  • q.empty() 判断是否为空

以上就是优先队列的基本操作,可以看出它和栈、队列的操作是一样的,无痛学习。

1.4  如何设置排序规则

假设我要为键值对排序,因此对队列定义如下:

priority_queue<pair<int, int>, vector<pair<int, int>>, decltype(& cmp)> q(cmp);

其中,pair<int, int> 的第一个 int 是键,第二个 int 是值。要求 cmp 是一个函数指针(即函数的地址),decltype 是类型推导。

排序规则如下:

static bool cmp(pair<int, int> m, pair<int, int> n) {
    return m.second > n.second;
}

其中,m.second 和 n.second 分别代表键值对 m 和 n 的值。

Q:为什么要加 static?

A:因为不加会报错:

  • “error: must explicitly qualify name of member function when taking its address”
  • “错误:在取成员函数的地址时必须显式限定成员函数的名称”

为什么是 m.second > n.second?这样看起来不是谁的值更大,谁排前面吗?答:我也不知道啊。

2  347. 前 K 个高频元素

好不容易通过  215. 数组中的第 K 个最大元素  学会了堆排序,准备大展身手,结果运行超时。。

解题思路:

  1. 遍历 nums 并使用 hash 为其中的所有数字计数
  2. 遍历 hash 并使用 priority_queue 为其中的计数结果排序

2.1  第 2 步的具体实现

① 定义优先队列:

priority_queue<pair<int, int>, vector<pair<int, int>>, decltype(& cmp)> q(cmp);

为什么存储的数据类型是 pair<int, int>?因为我们要同时存储 “数字” 和它的 “个数”,否则后面把 “个数” 的顺序排出来了,却不知道这些 “个数” 对应的 “数字” 是哪些。

② 遍历 hash 并使用 priority_queue 为其中的计数结果排序:

  • 依次向 priority_queue 插入数据,但限制 priority_queue 的大小最终为 k
  • 若当前元素的 count 比队首元素的 count 大,那么弹出队首,换当前元素进去
for (auto & [num, count] : hash) {
    if (q.size() < k) {
        q.push(make_pair(num, count));
    } else if (q.size() == k) {
        if (count > q.top().second) {
            q.pop();
            q.push(make_pair(num, count));
        }
    }
}

2.2  举例说明

这里用字母只是为了方便区分,原题还是用的数字

  • 假设 nums = [a, b, b, c, c, c], k = 2
  • 易得 hash = [(a, 1), (b, 2), (c, 3)]

下面来看 priority_queue 是怎么操作的:

第 1 时刻,队列大小显然没有到达 k,因此直接插入键值对。第 2 时刻,队列大小显然也没有到达 k,因此直接插入键值对。第 3 时刻,由于席位已满,因此必须判断谁更配进入队列。由于 a 的个数被 c 的个数少,因此 a 被弹出队列为 c 让位。

注意:每当有新元素进入队列的时候,队列都会按照之前设置的排序规则 cmp 为元素重新排队。

2.3  完整代码
class Solution {
public:
    // 排序规则
    static bool cmp(pair<int, int> m, pair<int, int> n) {
        return m.second > n.second;
    }

    vector<int> topKFrequent(vector<int>& nums, int k) {
        // 计数
        unordered_map<int, int> hash;
        for (auto & n : nums) {
            hash[n]++;
        }

        // 排序
        priority_queue<pair<int, int>, vector<pair<int, int>>,
                       decltype(& cmp)> q(cmp);
        for (auto & [num, count] : hash) {
            if (q.size() < k) {
                q.push(make_pair(num, count));
            } else if (q.size() == k) {
                if (count > q.top().second) {
                    q.pop();
                    q.push(make_pair(num, count));
                }
            }
        }

        // 处理结果
        vector<int> ans;
        while (!q.empty()) {
            ans.push_back(q.top().first);
            q.pop();
        }

        return ans;
    }
};

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

模仿  347. 前 K 个高频元素  即可

class Solution {
public:
    static bool cmp(int a, int b) {
        return a > b;
    }

    int findKthLargest(vector<int>& nums, int k) {
        priority_queue<int, vector<int>, decltype(& cmp)> q(cmp);

        for (auto & n : nums) {
            if (q.size() < k) {
                q.push(n);
            } else if (q.size() == k) {
                if (n > q.top()) {
                    q.pop();
                    q.push(n);
                }
            }
        }

        return q.top();
    }
};


当你有了优先队列,谁还看堆排 (〃>皿<)

相关推荐

  1. Leetcode100

    2024-03-24 03:26:01       35 阅读
  2. LeetCode100

    2024-03-24 03:26:01       9 阅读
  3. Leetcode100

    2024-03-24 03:26:01       6 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-03-24 03:26:01       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-03-24 03:26:01       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-03-24 03:26:01       18 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-03-24 03:26:01       20 阅读

热门阅读

  1. 素数问题 python

    2024-03-24 03:26:01       17 阅读
  2. 家庭影院组成原理

    2024-03-24 03:26:01       19 阅读
  3. openSSH学习

    2024-03-24 03:26:01       19 阅读
  4. 架构设计常用到的10种设计模

    2024-03-24 03:26:01       17 阅读
  5. 05- 还在双引号添加字符串?- 文本块

    2024-03-24 03:26:01       20 阅读
  6. vue3中reactive详解

    2024-03-24 03:26:01       23 阅读
  7. 前端视角如何理解“时间复杂度O(n)”

    2024-03-24 03:26:01       17 阅读