今日总结:
整体简单,需要复习栈与队列的使用场景:先进后出、先进先出,
需要复习栈与队列常用的一些方法
栈的基础操作:
1、压栈push()
2、弹栈pop()
3、查看栈顶:top()
4、栈是否为空 empty()
队列的基础操作:
1、入队push()
2、出队pop()
3、查看队头front()
4、查看队尾back()
5、队列是否为空 empty()
6、队列大小size()
栈与队列基础
栈的声明:
#include <stack>
stack <类型> 名称
push向栈顶插入元素
pop的弹出栈顶元素
栈不是容器,而是容器适配器(栈的底层是通过其他的容器实现的,对外提供统一的接口)
栈的底层容器可以是:vector、deque、list,如果没有指明具体的底层实现,默认使用deque双端队列为栈的底层结构,也可以自行指定栈的底层容器:
std :: stack<int,std :: vector<int>> third //使用vector为底层容器实现栈
栈的特点是先进后出,没有迭代器,不能遍历所有元素
使用数组实现栈
//定义栈的数组为stk,tt为栈顶的下标,初始为0
int stk[N],tt=0;
//栈顶插入一个元素
void add(int x)
{
stk[++ tt] =x;
}
//栈顶删除一个元素pop
void remove()
{
tt--;
}
//判断栈是否为空
void is_empty()
{
if(tt>0)printf("不空");
else printf("空");
}
//获取栈顶元素
void query()
{
printf("%d",stk[tt]);
}
队列
先进先出,也是容器适配器 在默认情况下也是使用deque实现的,也可以通过list等实现
std :: queue<int,std :: list<int>> third //使用list为底层容器实现队列
双端队列:
*#include <deque>头文件deque包含双端队列
deque是双端队列,支持在两端高效插入和删除元素的连续线性存储空间,像是vector与queue的组合,与vector相比,deque在头部增删元素仅需O(1)的时间,与queue相比,deque像数组一样支持随机访问。
可以尾插、头插、队尾弹出、队头弹出,支持随机访问[]
begin/end,返回头/尾迭代器
front/back队头/队尾元素
push_back从队尾入队
push_front从队头入队
pop_back从队尾弹出
pop_front从队头弹出
clear清空队列
用栈实现队列
题目链接:LeetCode232、用栈实现队列
整体思路:
栈:先进后出
队列:先进先出
所以可以使用两个栈,一个用于接收输入的值,一个用于输出队头的值
当要输入时,直接将值输入到接收栈的栈顶
当要输出时,如果输出栈有值就输出栈顶并删除,如果输出栈为空,将输入栈的所有值从栈顶弹出,让输出栈全部接收,从而实现元素从输入栈的栈尾到输出栈的栈顶的翻转,实现先进先出
class MyQueue {
public:
//定义一个入栈,一个转换的出栈
stack <int> stkin;
stack <int> stkout;
MyQueue() {
}
void push(int x) {
stkin.push(x);
}
int pop() {
int top = this->peek();
stkout.pop();
return top;
}
int peek() {
if(stkout.empty())
{
while(!stkin.empty())
{
stkout.push(stkin.top());
stkin.pop();
}
}
return stkout.top();
}
bool empty() {
if(stkin.empty()&&stkout.empty())
{
return true;
}
else return false;
}
};
/**
* Your MyQueue object will be instantiated and called as such:
* MyQueue* obj = new MyQueue();
* obj->push(x);
* int param_2 = obj->pop();
* int param_3 = obj->peek();
* bool param_4 = obj->empty();
*/
LeetCode225、用队列实现栈
题目链接:
整体思路:
使用两个队列,队列A用于正常记录入栈,队列B用于在弹出栈顶、获取栈顶元素的时候,将A的除去最后的队尾元素全部加入到B中,然后获取A的队头元素,之后再将B的元素返还给A
class MyStack {
//没有好的方法,使用两个队列,队列A用于接收元素
//队列B用于在删除、获取栈顶元素时,将队列A中除最后一个元素全部移动到队列B,将A中队头输出
public:
//定义两个队列
queue <int> A;
queue <int> B;
MyStack() {
}
void push(int x) {
A.push(x);
}
int pop() {
int size = A.size()-1;
while(size--)
{
B.push(A.front());
A.pop();
}
//A剩余一个元素
int C = A.front();
// B.push(A.front());
A.pop();
while(!B.empty())
{
A.push(B.front());
B.pop();
}
return C;
}
int top() {
int size = A.size()-1;
while(size--)
{
B.push(A.front());
A.pop();
}
//A剩余一个元素
int C = A.front();
B.push(A.front());
A.pop();
while(!B.empty())
{
A.push(B.front());
B.pop();
}
return C;
}
bool empty() {
return A.empty();//如果A是空的表示没有元素了,因为B只是记录一下,最终还是会回到A
}
};
/**
* Your MyStack object will be instantiated and called as such:
* MyStack* obj = new MyStack();
* obj->push(x);
* int param_2 = obj->pop();
* int param_3 = obj->top();
* bool param_4 = obj->empty();
*/
有效的括号
题目链接:LeetCode20、有效的括号
整体思路:
因为是做括号匹配,所以可以将三种左括号放到栈中,当遇到右括号的时候与栈顶元素比较,如果匹配,栈顶弹出,不匹配说明出错
但是需要注意细节问题:
1、如果出现右括号就直接与栈顶比较会出现问题:栈是空的,没有栈顶-->加上限制条件,栈不为空的时候才能与右括号比较,栈为空,出现右括号直接返回false
2、因为只有右括号匹不匹配,才会进行判断返不返回false,所以丢失了一种情况:在遍历完字符串后,栈中仍然有没有匹配的左括号-->在遍历完字符串后,需要判断栈是不是空栈。
class Solution {
//使用栈的先进后出,从左到右获取括号
//在获取括号时,判断与前一位是不是相同,不同就加入栈顶,相同就弹出栈顶,
//遍历完之后,查看栈是不是空的
public:
bool isValid(string s) {
stack<char>stk;
for(auto i :s)
{
if ((i=='('||i=='['||i=='{')) stk.push(i);//如果是左括号直接入栈
else if(!stk.empty()&&i==')'&&stk.top()=='(')//匹配成功
stk.pop();//删除栈顶
else if(!stk.empty()&&i==']'&&stk.top()=='[')//匹配成功
stk.pop();//删除栈顶
else if(!stk.empty()&&i=='}'&&stk.top()=='{')//匹配成功
stk.pop();//删除栈顶
else return false;
}
return stk.empty();//如果栈中没有元素就是全部匹配
}
};
删除字符串中所有相邻重复项
题目链接:LeetCode1047、删除字符串中所有相邻重复项
整体思路:
使用一个栈来判断栈顶和当前元素,整体简单
class Solution {
public:
string removeDuplicates(string s) {
//不相同就入栈,相同就不入栈,栈顶元素弹出
stack <int> stk;
for(auto i :s)
{
if(stk.empty())//空就直接入栈
stk.push(i);
else if(i==stk.top())
{
stk.pop();
}
else stk.push(i);
}
//需要返回,栈底在前
string res;
while(!stk.empty())
{
res +=stk.top() ;
stk.pop();
}
reverse(res.begin(),res.end());
return res;
}
};