目录
0. 引言
1. priority_queue 介绍
1.1 构造函数
1.2 priority_queue 接口函数使用
1.3 仿函数
1.4 题目练习
2. priority_queue 模拟实现
2.1基本框架:
2.2 默认构造函数
2.3 基本函数
2.4 堆的向上以及向下调整
0. 引言
优先队列 (priority_queue) 是一种容器适配器,根据严格的弱排序标准,它的第一个元素总是它所包含的元素中最大的。类似于堆,在堆中可以随时插入元素,并且只能检索最大堆元素(优先队列中位于顶部的元素)。优先队列和堆本质是一样的,以数组形式存储的完全二叉树。
1. priority_queue 介绍
1.1 构造函数
我们可以看到有两种构造方式,一个是构造一个空对象,另一个是通过迭代器的区间来构造,默认的构造出的是大堆。
priority_queue<int> pq1; //直接构造空对象
接下来我们分别以大堆和小堆的方式来构造对象:
vector<int> v1 = {3,2,7,6,0,4,1,9,8,5};priority_queue<int, vector<int>, less<int>> pq1(v1.begin(), v1.end());//less-大堆while (!pq1.empty()){cout << pq1.top() << " ";pq1.pop();}cout << endl;priority_queue<int, vector<int>, greater<int>> pq2(v1.begin(), v1.end());//greater-小堆while (!pq2.empty()){cout << pq2.top() << " ";pq2.pop();}cout << endl;priority_queue<int> pq3(v1.begin(), v1.end());//默认大堆while (!pq3.empty()){cout << pq3.top() << " ";pq3.pop();}cout << endl;
因此我们得出: less - 大堆, greater - 小堆。
1.2 priority_queue 接口函数使用
接口函数主要包括以下:
函数 | 说明 |
empty | 检测优先级队列是否为空,是返回true,否则返回 false |
top | 返回优先级队列中最大(最小元素),即堆顶元 |
push | 在优先级队列中插入元素x |
pop | 删除优先级队列中最大(最小)元素,即堆顶元素 |
1.3 仿函数
仿函数又名函数对象 function objects 仿函数的主要作用是借助类和运算符重载,做到同一格式兼容所有函数。由于模板将 less 用作大堆,而 greater 用做小堆,是在有点别扭,如果是我们自己实现仿函数的化,肯定会按照习惯来写,less 表示小堆,greater 表示大堆。例如:
template<class T>
struct less
{bool operator()(const T& x, const T& y){return x < y;}
};template<class T>
struct greater
{bool operator()(const T& x, const T& y){return x > y;}
};
1.4 题目练习
优先级队列适合来进行TOPK 以及 排序问题,因为其底层是和堆一模一样的。现在我们一起来看下面这道题:
这题如果不关心时间复杂度,直接利用 sort 排序将会很简单:
class Solution {
public:int findKthLargest(vector<int>& nums, int k) {sort(nums.begin(),nums.end());return nums[nums.size()-k]; }
};
当我们使用优先级队列时,时间复杂度会更好:
//大堆
class Solution {
public:int findKthLargest(vector<int>& nums, int k) {priority_queue<int> pq1(nums.begin(), nums.end());for(int i = 0;i < k-1; i++){pq1.pop();}return pq1.top(); }
};
//小堆
class Solution {
public:int findKthLargest(vector<int>& nums, int k) {priority_queue<int,vector<int>, greater<int>> pq1(nums.begin(), nums.begin()+k);for(int i = k ;i < nums.size(); i++){if(nums[i] > pq1.top()){pq1.pop();pq1.push(nums[i]);}}return pq1.top(); }
};
2. priority_queue 模拟实现
优先级队列的模拟实现,难点在于堆的 向上和向下调整。
2.1基本框架:
#pragma once#include <vector>namespace LHY
{//默认底层结构为 vectortemplate<class T, class Container = std::vector<T>>class priority_queue{public://构造函数及其他功能private:Container _con; //其中的成员变量为底层容器对象};
}
2.2 默认构造函数
//默认构造函数
priority_queue():_con()
{}//迭代器区间构造
template<class InputIterator>
priority_queue(InputIterator first, InputIterator last):_con()
{while (first != last){push(*first);first++;}
}
2.3 基本函数
//判断是否为空
bool empty() const
{return _con.empty();
}//优先级队列的大小(有效元素数)
size_t size() const
{return _con.size();
}//堆顶元素(优先级最 高/低 的值)
const T& top() const
{return _con.front();
}
2.4 堆的向上以及向下调整
//向上调整
void adjust_up(size_t child)
{size_t parent = (child - 1) / 2;while (child != 0){//父 > 子 此时为大堆,如果不符合,则调整if (_con[child] > _con[parent]){std::swap(_con[child], _con[parent]);child = parent;parent = (child - 1) / 2;}elsebreak;}
}
//向下调整
void adjust_down(size_t parent)
{size_t child = parent * 2 + 1; //假设左孩子为 【大孩子 / 小孩子】while (child < size()){//判断右孩子是否比左孩子更符合条件,如果是,则切换为与右孩子进行比较if (child + 1 < size() && _con[child + 1] > _con[child])child++;//父 > 子 此时为大堆,如果不符合,则调整if (_con[child] > _con[parent]){std::swap(_con[child], _con[parent]);parent = child;child = parent * 2 + 1;}elsebreak; //满足条件时,一样需要跳出,不再调整}
}