一、栈和队列的行为特征
栈和队列与数组类似,但对于插入、访问、删除都有一定的限制。可将元素插入到什么位置或从什么位置删除决定了容器的行为特征。
1.1 栈
栈是LIFO(后入先出)的容器,只能从栈顶插入或删除元素。可以将栈视为一叠盘子,最后放上去的盘子将最先被取下来。在C++中泛型STL容器std::stack模拟了栈的这种行为,需要包含如下头文件。
#include <stack>
1.2 队列
队列是FIFO(先入先出)的容器,元素被插入到队尾,最先插入的元素最先删除。可将队列视为食堂窗口买饭的人,在没有插队的情况下,先加入队列的人先离开。要在C++中创建和使用队列容器需要包含如下头文件。
#include <queue>
二、使用STL stack类
STL stack是一个模板类,允许在顶部插入和删除元素。
2.1 实例化
std::stack的定义如下:
template<
class elementType,
class Container=deque<Type>
> class stack;
参数elementType是stack存储的对象类型,第二个参数Container是stack使用的默认底层容器实现类。stack默认使用std::deque来存储数据,但可以指定使用vector或list来存储数据。实例化栈的代码类似于下面这样:
// 默认实例化整型栈
std::stack<int> stk;
// 创建自定义类对象的栈
std::stack<MyClass> stk;
// 创建不同底层容器的栈
std::stack<double, vector<double>> stk;
2.2 成员函数
stack改变了另一种容器(如deque、list、vector)的行为,通过限制元素插入或删除的方式实现其功能。下表解释了stack的公有成员函数并演示了如何将这些函数用于整型栈。
函数 | 描述 |
---|---|
push() | 在栈顶插入元素,例如stk.push(25); |
pop() | 删除栈顶的元素,例如stk.pop(); |
empty() | 检查栈是否为空并返回bool类型,例如bool res = stk.empty(); |
size() | 返回栈中的元素个数,例如int num = stk.size(); |
top() | 获取指向栈顶元素的引用,例如int n = stk.top(); |
三、使用STL queue类
queue是一个泛型类,只允许在末尾插入元素,从开头删除元素。queue不允许访问中间的元素,但可以访问开头和末尾的元素。
3.1 实例化
std::queue的定义如下:
template<
class elementType,
class Container=deque<Type>
> class queue;
其中elementType是queue对象包含的元素的类型。Container是std::queue用于存储其数据的集合类型,可以将该模板参数设置为list、vector、deque。默认为deque。
实例化queue的方式如下:
// 最基础的实例化
std::queue<int> que;
// 修改容器类型
std::queue<double, list<double>> dQue;
// 使用现有的queue来实例化新的queue
std::queue<int> copyQue(que);
3.2 成员函数
std::queue的实现也是基于STL容器vector、list、deque的。其成员函数如下表所示:
函数 | 描述 |
---|---|
push() | 在队尾(即最后一个位置)插入一个元素。 |
pop() | 将队首(即最开始位置)的元素删除。 |
front() | 返回指向队首元素的引用。 |
back() | 返回指向队尾元素(即最后插入的元素)的引用。 |
empty() | 检查队列是否为空并返回一个bool类型。 |
size() | 返回队列中的元素数。 |
值得注意的是,queue并没有提供begin()和end()等函数,这是有意为之的,旨在只允许对queue执行符合队列行为特征的操作。
四、使用STL优先级队列
priority_queue与queue的不同之处在于,包含最大值(或二元谓词认为是最大值)的元素位于队首,且只能在队首执行操作。
4.1 实例化
std::priority_queue类的定义如下:
template<
class elementType,
class Container=vector<Type>,
class Compare=less<typename Container::value_type>
> class priority_queue;
其中elementType是一个模板参数,指定了优先级队列将包含的元素的类型。第二个模板参数指定priority_queue在内部将使用哪个集合类来存储数据。第三个参数能够指定一个二元谓词,以帮助队列判断哪个元素应位于队首。如果没有指定二元谓词,priority_queue类将默认使用二元谓词std::less进行排序(新插入节点小于父节点),这将创建出大顶堆,即父节点比子节点大,而使用std::greater(新插入节点大于父节点)将创建小顶堆,即父节点比子节点小。
实例化priority_queue可采用如下方式:
// 默认创建大顶堆
std::priority_queue<int> pq1;
// 创建小顶堆
std::priority_queue<int, deque<int>, greater<int>> pq2;
// 拷贝
std::priority_queue<int> copyQ(pq1);
4.2 成员函数
queue提供了成员函数front()和back(),但priority_queue并没有。下表展示了priority_queue的成员函数与其功能。
函数 | 描述 |
---|---|
push() | 在优先级队列中插入一个元素。 |
pop() | 删除队首的元素,默认为最大元素。 |
top() | 返回指向队列中队首元素的引用。 |
empty() | 检查优先级队列是否为空并返回一个bool类型。 |
size() | 返回优先级队列中的元素个数。 |
4.3 具体应用
题目描述:给定整数数组 nums 和整数 k,请返回数组中第 k 个最大的元素。请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素。你必须设计并实现时间复杂度为 O(n) 的算法解决此问题。
示例 1:
输入: [3,2,1,5,6,4], k = 2 输出: 5
示例 2:
输入: [3,2,3,1,2,4,5,5,6], k = 4 输出: 4
题目要求时间复杂度为 O(n) ,因此可以尝试使用priority_queue构建大顶堆实现堆排序选择出第 k 个最大的元素。代码如下所示:
class Solution {
public:
int findKthLargest(vector<int>& nums, int k) {
// 构建大顶堆
priority_queue<int, vector<int>, less<int>> pq;
// 向大顶堆中添加元素
for(int i=0; i<nums.size(); i++) {
pq.push(nums[i]);
}
// 将前k-1个元素出队
for(int i=0; i<k-1; i++) {
pq.pop();
}
// 返回堆顶的元素
return pq.top();
}
};
测试提交后所有测试用例均可通过。