1.栈与队列理论基础
队列是先进先出,栈是先进后出
1.1栈
栈是以底层容器来完成所有的工作,对外提供统一的接口,底层容易使可插拔的(可以控制使用哪种底层容易来实现栈的功能)
所以STL中栈往往不被归类为容器,而被归类为container adapter(容器适配器)。
1.1.1C++stack(栈)
在计算机科学领域,我们致力于各种程序。他们每个人都有自己的域和实用程序。根据程序创建的目的和环境,我们有大量数据结构可供选择。其中之一是“堆栈”。在讨论这种数据类型之前,让我们看一下它的语法。
- 语法
template<class T, class Container = deque<T> > class stack;
这种数据结构使用LIFO技术,其中LIFO表示后进先出。首先插入的元素将在末尾提取,以此类推。有一个名为“top”的元素,它是位于最上面位置的元素。所有插入和删除操作都是在堆栈的顶部元素本身进行的。
应用区域中的堆栈暗示为容器适配器。
容器应支持下列操作列表
-
成员类型
-
函数
借助函数,可以在编程领域中使用对象或变量。堆栈提供了大量可以在程序中使用或嵌入的函数。相同的列表如下:
1.2队列
1.2.1C++queue(队列)
此数据结构适用于FIFO技术,其中FIFO表示先进先出。首先插入的元素将首先被提取,依此类推。有一个称为“前”的元素,它是位于最前位置或位于第一个位置的元素,也有一个名为“后”的元素,它是位于最后位置的元素。在普通队列中,元素的插入在尾部,而删除则从前面开始。
函数
1.2.2C++优先队列(priority_queue)
C ++中的优先队列是STL中的派生容器,它仅考虑最高优先级元素。队列遵循FIFO策略,而优先队列根据优先级弹出元素,即,优先级最高的元素首先弹出。
它在某些方面类似于普通队列,但在以下方面有所不同:
在优先队列中,队列中的每个元素都与某个优先级相关联,但是优先级在队列数据结构中不存在。
优先队列中具有最高优先级的元素将被首先删除,而队列遵循FIFO(先进先出)策略,这意味着先插入的元素将被首先删除。
如果存在多个具有相同优先级的元素,则将考虑该元素在队列中的顺序。
优先队列是一种特殊的队列,这种队列会自动的把队列里的数排序(默认从大到小,使用“<”判断),而且还可以把数按照特定的方法排列!(包括结构体和重载"<")
在上图中,我们通过使用push()函数插入了元素,并且插入操作与普通队列相同。但是,当我们使用pop()函数从队列中删除元素时,优先级最高的元素将首先被删除。
例:
#include <iostream>
#include <queue>
#include <vector>
#include <functional>
using namespace std;
int main() {
// 创建一个优先队列,默认是最大堆
priority_queue<int> pq;
// 插入元素
pq.push(10);
pq.push(30);
pq.push(20);
pq.push(5);
pq.push(1);
// 输出并移除优先队列中的元素
cout << "优先队列中的元素按优先级顺序(大顶堆)出队:" << endl;
while (!pq.empty()) {
cout << pq.top() << " "; // 输出队列顶部元素
pq.pop(); // 移除顶部元素
}
cout << endl;
// 创建一个优先队列(最小堆)
priority_queue<int, vector<int>, greater<int>> minHeap;
// 插入元素
minHeap.push(10);
minHeap.push(30);
minHeap.push(20);
minHeap.push(5);
minHeap.push(1);
// 输出并移除优先队列中的元素
cout << "优先队列中的元素按优先级顺序(小顶堆)出队:" << endl;
while (!minHeap.empty()) {
cout << minHeap.top() << " ";
minHeap.pop();
}
cout << endl;
return 0;
}
输出:
优先队列中的元素按优先级顺序(大顶堆)出队:
30 20 10 5 1
优先队列中的元素按优先级顺序(小顶堆)出队:
1 5 10 20 30
函数
1.2.3C++deque(双端队列)
双端队列即可以从前端或后端的两段进行插入和删除
函数
2.相关算法题
2.1用栈实现队列(*)
class MyQueue {
public:
stack<int> st_in;
stack<int> st_out;
//初始化你的数据结构
MyQueue()
{
}
void push(int x)
{
st_in.push(x);
}
int pop()
{
//当st_out为空的时候,再从st_int里导入数据
if(st_out.empty())
{
while(!st_in.empty())
{
st_out.push(st_in.top());
st_in.pop();
}
}
int result = st_out.top();
st_out.pop();
return result;
}
int peek()
{
int res = this->pop();//直接使用已有的pop函数
//this是一个隐含指针,指向调用成员变量和成员函数
st_out.push(res); //因为pop函数弹出了元素res,所以再添加回去
return res;
}
bool empty()
{
return st_in.empty() && st_out.empty();
}
};
这里的this是一个隐含的指针,指向调用成员函数的对象本身,它可以在类的成员函数内部使用,用于访问该对象的成员变量和成员函数
2.2用队列实现栈(*)
class MyStack
{
public:
queue<int> que1;
queue<int> que2; //辅助队列,用来备份
MyStack()
{
}
void push(int x)
{
que1.push(x);
}
int pop()
{
int size = que1.size();
size--;
while(size--)
{
que2.push(que1.front());
que1.pop();
}
int result = que1.front();
que1.pop();
que1 = que2; //再将que2值赋给que1
while(!que2.empty()) //清空que2
{
que2.pop();
}
return result;
}
int top()
{
return que1.back();
}
bool empty()
{
return que1.empty();
}
};
2.3有效的括号
class Solution
{
public:
bool isValid(string s)
{
unordered_map<char, char> mp = {{')', '('}, {']', '['}, {'}', '{'}};
stack<char> st;
for(int i = 0; i < s.size(); i++)
{
if (s[i] == '{' || s[i] == '[' || s[i] == '(')
{
st.push(s[i]);
}
else
{
if (!st.empty())
{
char ch = st.top();
if (ch != mp[s[i]])
{
return false;
}
else
{
st.pop();
}
}
else
{
return false;
}
}
}
return st.empty();
}
};
2.4删除字符串中的所有相邻重复项
class Solution
{
public:
string removeDuplicates(string s)
{
stack<char> st;
for (char ch : s)
{
if (st.empty())
{
st.push(ch);
}
else
{
if (st.top() == ch)
{
st.pop();
}
else
{
st.push(ch);
}
}
}
string res;
while(!st.empty())
{
res += st.top();
st.pop();
}
reverse(res.begin(), res.end());
return res;
}
};
2.5 逆波兰表达式求值
class Solution
{
public:
int evalRPN(vector<string>& tokens)
{
unordered_set<string> oper_set = {"+", "-", "*", "/"};
stack<int> st;
for (int i = 0; i < tokens.size(); i++)
{
if(oper_set.find(tokens[i]) == oper_set.end())
{
st.push(stoi(tokens[i]));
}
else
{
int num2 = st.top();
st.pop();
int num1 = st.top();
st.pop();
if (tokens[i] == "+")
{
int num = num1 + num2;
st.push(num);
}
else if (tokens[i] == "-")
{
int num = num1 - num2;
st.push(num);
}
else if(tokens[i] == "*")
{
int num = num1 * num2;
st.push(num);
}
else
{
int num = num1 / num2;
st.push(num);
}
}
}
int res = st.top();
return res;
}
};
2.6滑动窗口最大值(*)
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k)
{
if(nums.size() == 0 || k == 0) return {};
deque<int> deque;
vector<int> res(nums.size() - k + 1);
for(int j = 0, i = 1 - k; j < nums.size(); i++, j++) {
// 删除 deque 中对应的 nums[i-1]
if(i > 0 && deque.front() == nums[i - 1])
deque.pop_front();
// 保持 deque 递减
while(!deque.empty() && deque.back() < nums[j])
deque.pop_back();
deque.push_back(nums[j]);
// 记录窗口最大值
if(i >= 0)
res[i] = deque.front();
}
return res;
}
};
注意i从1-k开始!!!,即从第一个元素开始加入队列
2.7前k个高频元素(*)
题目
哈希表+优先队列
#include <iostream>
#include <vector>
#include <unordered_map>
#include <queue>
using namespace std;
class Solution {
public:
vector<int> topKFrequent(vector<int>& nums, int k)
{
unordered_map<int, int> um;
for(auto& i : nums)
{
++um[i];
}
priority_queue<pair<int, int>> pq;
for(auto& i : um)
{
pq.push({i.second, i.first});
}
vector<int> res;
while(k--)
{
res.push_back(pq.top().second);
pq.pop();
}
return res;
}
};