栈与队列基础:
栈和队列的基本思想:栈是先进后出(Fisrt in Last out, FILO) 的,而队列是先进先出(Fisrt in Last out, FILO) 。栈只有栈顶top可以被外界访问,所以栈是没有迭代器,也是不支持随机访问的,即栈是不支持遍历操作的。
队列只有队头和队尾可以被外界访问的,同样也是不支持随机访问,没有迭代器。
c++中的stack和queue到底是不是容器呢?
不是容器。栈和队列可以理解为一种思想,而具体的实现通过vector,list以及deque都可以实现。而c++中的stack以及queue只能说是容器适配器而不是容器,通常使用deque实现。
代码随想录day11-栈与队列(1)
1、LeetCode 232 用栈实现队列
题目分析:
本题本身不考虑什么算法,考虑对栈和队列的熟悉情况。我们使用后两个栈来实现,一个作为输入栈,一个作为输出栈。输入栈存放数据,输出栈是输入栈反着存的数据,用来模拟队列的先进先出。
题目解答:
class MyQueue {
public:
MyQueue() {
}
void push(int x) {
stkIn.push(x);
}
int pop() {
// 将stkIn的东西存到stkOut中
if (stkOut.empty()) { // 当stkIn为空的时候,才往里面压入数据
while (!stkIn.empty()) {
stkOut.push(stkIn.top());
stkIn.pop();
}
}
int res = stkOut.top();
stkOut.pop();
return res;
}
int peek() {
// 使用pop()的方法
int res = pop();
stkOut.push(res); // 由于使用pop将其弹出来了,这里需要再压进去
return res;
}
bool empty() {
return stkOut.empty() && stkIn.empty(); // 二者都为空才是空
}
private:
stack<int> stkIn; // 输入的栈
stack<int> stkOut; // 输出的栈
};
2、LeetCode 225 用队列实现栈
题目分析:
题目要求使用两个队列来实现栈,其实使用一个队列就可以解决了。使用两个队列,第二个就是对第一个队列的暂存。
题目解答:
使用两个队列的思路:
class MyStack {
public:
MyStack() {
}
void push(int x) {
que1.push(x);
}
int pop() {
int n = que1.size();
n--; // 保留最后一个弹出并返回
while (n) {
que2.push(que1.front());
que1.pop();
n--;
}
// 此时就是最后一个
int res = que1.front();
que1.pop();
que1 = que2; // 将que2赋值给que1
while (!que2.empty()) que2.pop();
return res;
}
int top() {
return que1.back();
}
bool empty() {
return que1.empty();
}
private:
queue<int> que1;
queue<int> que2;
};
其实以上代码用一个队列完全可以实现,并且实现的方法完全一样。
class MyStack {
public:
// 尝试使用一个队列来实现
MyStack() {
}
void push(int x) {
que.push(x);
}
int pop() {
// 其实需要弹出栈顶,对于队列而言,就是其最后一个,我们只需要把前面的所有元素都移到后面去就行
int size = que.size();
size--; // 提前留好一个元素
while (size--) {
que.push(que.front()); // 将后面的元素再搞到后面去
que.pop();
}
int res = que.front(); // 此时第一个就是需要弹出的元素
que.pop();
return res;
}
int top() {
return que.back();
}
bool empty() {
return que.empty();
}
private:
queue<int> que;
};
3、LeetCode 20 有效的括号
题目分析:
其实这道题目非常简单,就是一直不想思考,心烦意乱。
本题就是判断括号是不是符合规定,其实我们遇到左括号入栈,遇到右括号判断栈顶的是不是匹配,如果不匹配或者直接没元素了,就返回false。否则栈为空,就代表是符合规定的。
注意细节:如果是奇数的话,直接返回false即可。
题目解答:
class Solution {
public:
bool isValid(string s) {
stack<char> stk;
if (s.length() % 2 != 0) return false;
for (int i = 0; i < s.length(); i++) {
if (s[i] == '(' || s[i] == '[' || s[i] == '{') stk.push(s[i]);
else if (s[i] == ')') {
// 如果没有左括号或者左括号不匹配,直接返回false
if (stk.empty() || stk.top() != '(') return false;
else stk.pop();
}
else if (s[i] == ']') {
if (stk.empty() || stk.top() != '[') return false;
else stk.pop();
}
else {
if (stk.empty() || stk.top() != '{') return false;
else stk.pop();
}
}
return stk.empty();
}
};
卡尔的思路比较巧妙,他将不符合情况的题目分为三种,即
- 第一种情况:字符串里左方向的括号多余了 ,所以不匹配,如( ( ){ }[ ];
- 第一种情况:括号数量对的,但是不匹配,如( [ ) ];
- 第三种情况:字符串里右方向的括号多余了 ,所以不匹配,如 ( ){ }[ ] ) )。
然后在遇到左括号的时候,压入相应的右括号,最后只需要判读是不是和栈顶元素相等即可,简化了左括号入栈的代码。解答如下:
class Solution {
public:
bool isValid(string s) {
// 使用栈的思路来解决问题
stack<char> stk;
if (s.length() % 2 != 0) return false; // 如果是奇数,直接返回即可
for (int i = 0; i < s.size(); i++) {
if (s[i] == '(') stk.push(')'); // 将其匹配的括号压入栈,只需要判断想不想等即可,漂亮的思路呢
else if (s[i] == '[') stk.push(']');
else if (s[i] == '{') stk.push('}');
// 下面的else if就是右括号的情况了
// 下面的语句包括了第二,第三种情况
else if (stk.empty() || s[i] != stk.top()) return false;
else stk.pop(); // 如果相等,就弹出
}
return stk.empty(); // 第一种情况,如果栈为空,就代表匹配上了,否则就不为空,返回false
}
};
4、LeetCode 1047 删除字符串中的所有相邻重复项
题目分析:
如果不知道用栈的话,本题可以这样思考。可以使用我们之前学的滑动窗来思考。最开始两个指针都指向开始,然后右指针遍历数组,不重复的话,左指针也更新,一旦遇到了重复的,右指针移动,直到遇到不重复的,左指针移动。这里就牵涉到一个问题:
怎么确定当前是不是重复的呢,怎么存呢,如果是的话,刚刚没存怎么回去,如果存了,中间的元素一删除,又出现了元素怎么办?
这就是栈的优势所在了,不管重复与否,我先入栈,然后遇到了元素和栈顶的相等,就代表是重复的,弹出即可。最终剩在栈种的元素就是不重复的元素。
题目解答:
class Solution {
public:
string removeDuplicates(string s) {
// 典型的栈的思路解决问题
stack<int> stk;
string ans;
for (auto& ch : s) {
if (!stk.empty() && ch == stk.top()) stk.pop(); // 如果和栈顶元素相等,弹出
else stk.push(ch);
}
while (!stk.empty()) {
ans += stk.top();
stk.pop();
}
reverse(ans.begin(), ans.end()); // 出来的结果的反的
return ans;
}
};
直接用字符串实现栈,还可以省反转的时间:
class Solution {
public:
string removeDuplicates(string s) {
string ans;
for (const auto& ch : s) {
if (!ans.empty() && ans.back() == ch) ans.pop_back();
else ans.push_back(ch);
}
return ans;
}
};
string也是有empty(),back(),push_back()以及pop_back()的。
今天对栈和队列有了点儿初步的认知,主要就是两个性质的使用。
苦难是人生的老师。——巴尔扎克