队列
1. 简要介绍
队列是先进先出的数据结构,跟栈一样,队列可以用数组来实现,也可以用链表来实现。用数组实现的栈叫作顺序栈,用链表实现的栈叫作链式栈。同样,用数组实现的队列叫作顺序队列,用链表实现的队列叫作链式队列。
常用操作:
(1)数据存取:
push(elem);
//往队尾添加元素pop();
//从队头移除第一个元素back();
//返回最后一个元素front();
//返回第一个元素
(2)大小操作:
empty();
//判断堆栈是否为空size();
//返回栈的大小
2. Leetcode练习题
leetcode上关于队列的题目:225,1047,150,239,347
2.1 225.用队列实现栈
题目链接:225. 用队列实现栈 - 力扣(LeetCode)
分析:这种直接画图。用两个队列和一个队列都行,因为另一个队列的作用就是作为缓冲区,队列操作也比栈方便。
class MyStack {
public:
// push pop back front
queue<int> dIn;
queue<int> dOut; //这其实就是一个缓冲的stack
MyStack() {
}
void push(int x) {
dIn.push(x);
}
// 1.用两个queue实现的
// int pop() {
// int i = 0, n = dIn.size() - 1;
// while(i < n) {
// dOut.push(dIn.front());
// dIn.pop();
// i++;
// }
// int ret = dIn.front(); //最后一个数就是要删除的数字
// dIn.pop();
// while(! dOut.empty()) {
// dIn.push(dOut.front());
// dOut.pop();
// }
// return ret;
// }
// 2.用一个queue实现的
int pop() {
int i = 0, n = dIn.size() - 1;
while(i < n) {
dIn.push(dIn.front()); //直接把队头元素加到队尾
dIn.pop();
i++;
}
int ret = dIn.front(); //最后一个数就是要删除的数字
dIn.pop();
return ret;
}
int top() {
return dIn.back();
}
bool empty() {
return dIn.empty();
}
};
2.2 1047.删除字符串中的所有相邻重复项
题目链接:1047. 删除字符串中的所有相邻重复项 - 力扣(LeetCode)
分析:用一个栈就行,每次加入的时候进行一次比较,时间复杂度O(n)。最后的结果要reverse。
class Solution {
public:
string removeDuplicates(string s) {
stack<char> st;
int i = 0;
while(i < s.length()) {
if(st.empty() || s[i] != st.top()) {
st.push(s[i]);
} else {
st.pop();
}
i++;
}
string ret;
while(! st.empty()) {
ret.push_back(st.top());
st.pop();
}
reverse(ret.begin(), ret.end()); // 此时字符串需要反转一下
return ret;
}
};
2.3 150.逆波兰表达式求值
题目链接:150. 逆波兰表达式求值 - 力扣(LeetCode)
分析:遍历一遍,边遍历边进行计算。本题中得用long long 类型,之前用过stoi函数,是把字符串转化为int类型,这题用的是stoll函数,把字符串解析为long long 类型。
class Solution {
public:
int evalRPN(vector<string>& tokens) {
// stoll函数将在函数调用中作为参数提供的字符串转换为long long int。
// 它解析str并将其内容解释为指定基数的整数,并将其作为long long int类型的值返回。
stack<long long> st;
for(int i = 0; i < tokens.size(); i++) {
// 注意单引号是字符型,双引号是字符串型,把引号用错了一直报错
if(tokens[i] == "+" || tokens[i] == "-" || tokens[i] == "*" || tokens[i] == "/") {
long long num1 = st.top();
st.pop();
long long num2 = st.top();
st.pop();
if(tokens[i] == "+") st.push(num2 + num1);
if(tokens[i] == "-") st.push(num2 - num1);
if(tokens[i] == "*") st.push(num2 * num1);
if(tokens[i] == "/") st.push(num2 / num1);
} else {
st.push(stoll(tokens[i]));
}
}
return st.top();
}
};
2.4 239.滑动窗口最大值
题目链接:239. 滑动窗口最大值 - 力扣(LeetCode)
分析:用一个递减的双端队列dq来做:滑动窗口也就是遍历nums,如果加入的值大于dq中最后一个值,就pop它,保持dq的递减;这样dq的front就是要求的窗口中的最大值,加入到ans数组中;注意要把超出窗口的值pop掉。
class Solution {
public:
vector<int> maxSlidingWindow(vector<int>& nums, int k) {
vector<int> ans; //保存结果
deque<int> dq; //用一个双端队列,该队列是递减队列
for(int i = 0; i < nums.size(); i++) {
//保持递减
while(! dq.empty() && dq.back() < nums[i])
dq.pop_back();
dq.push_back(nums[i]);
//将窗口中最大的数也就是front的值加入ans中
if(i >= k - 1) {
ans.push_back(dq.front());
//超出窗口的数pop
if(nums[i - k + 1] == dq.front()) {
dq.pop_front();
}
}
}
return ans;
}
};
2.5 347.前 K 个高频元素
题目链接:347. 前 K 个高频元素 - 力扣(LeetCode)
分析:先用unordered_map统计元素出现的频率,之后用小顶堆,超出k之后的就pop掉,保留的就是前k个高频元素。注意要小顶堆的自定义函数中return的是左 > 右
,大顶堆是左 < 右
!优先级队列的定义与其他的正好是反过来的,要特别注意这点。
补充:
-
与基于比较的排序算法时间复杂度 O(nlogn)相比,使用堆,优先队列复杂度可以下降到 O(nlogk),在总体数据规模 n 较大,而维护规模 k 较小时,时间复杂度优化明显。
-
时间复杂度分析:O(nlogk)。首先,遍历一遍数组统计元素的频率,这一系列操作的时间复杂度是 O(n);接着,遍历用于存储元素频率的 map,如果元素的频率大于最小堆中顶部的元素,则将顶部的元素删除并将该元素加入堆中,这里维护堆的数目是 k,所以这一系列操作的时间复杂度是 O(nlogk)的;因此,总的时间复杂度是 O(nlogk)。
-
优先级队列:
priority_queue<Type, Container, Functional>;
-
Type是要存放的数据类型;
-
Container是实现底层堆的容器,必须是数组实现的容器,如vector、deque;
-
Functional是比较方式/比较函数/优先级。
priority_queue<Type>;'
此时默认的容器是vector,默认的比较方式是大顶堆less。
-
class Solution {
public:
vector<int> topKFrequent(vector<int>& nums, int k) {
unordered_map<int, int> map;
vector<int> ans;
//先遍历一遍记录元素出现的频率 O(n)
for(auto c : nums) {
map[c] ++;
}
//自定义比较
struct myComparison{
bool operator()(pair<int, int> &p1, pair<int,int> &p2) {
return p1.second > p2.second; //小顶堆用的是>,大顶堆用的是<
}
};
//优先级队列实现小顶堆
priority_queue<pair<int, int>, vector<pair<int, int>>, myComparison> q;
//多于k的元素pop出去 O(nlogk)
for(auto c : map) {
q.push(c);
if(q.size() > k) {
q.pop();
}
}
//加入到结果中 O(k)
while(! q.empty()) {
ans.push_back(q.top().first);
q.pop();
}
return ans;
}
};