栈和队列刷题集合
Posted 落禅
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了栈和队列刷题集合相关的知识,希望对你有一定的参考价值。
栈刷题集合
面试题 03.02. 栈的最小值
请设计一个栈,除了常规栈支持的pop与push函数以外,还支持min函数,该函数返回栈元素中的最小值。执行push、pop和min操作的时间复杂度必须为O(1)。
//示例:
MinStack minStack = new MinStack();
minStack.push(-2);
minStack.push(0);
minStack.push(-3);
minStack.getMin(); --> 返回 -3.
minStack.pop();
minStack.top(); --> 返回 0.
minStack.getMin(); --> 返回 -2.
本题思路:首先栈的push,top,操作我们可以直接用stl库函数,最麻烦的是min函数,而且时间复杂度为O(1),这个思想非常重要,定义一个栈s1,专门用来存放栈中的元素,完成栈的push,pop工作,然后定义一个辅助栈,来存放最小值,使得每一个s1对应的栈顶元素都有对应的最小值
class MinStack {
public:
/** initialize your data structure here. */
//1.定义两个栈,第一个栈用来存储值
//第二个栈用来寻找最小值
stack<int>s1;
stack<int>s2;
MinStack() {
//先将s2初始化
s2.push(INT_MAX);
}
void push(int x) {
//为s1赋值
s1.push(x);
//每次找到栈顶元素和输入元素的较小值进行比较,使得s1的每个主站元素都有其对应的最小值
s2.push(min(s2.top(),x));
}
void pop() {
//s1执行pop操作
//与之对应的最小值也执行pop操作,让每个栈顶元素与最小值进行对应
s1.pop();
s2.pop();
}
int top() {
return s1.top();
}
int getMin() {
return s2.top();
}
};
/**
\\* Your MinStack object will be instantiated and called as such:
\\* MinStack* obj = new MinStack();
\\* obj->push(x);
\\* obj->pop();
\\* int param_3 = obj->top();
\\* int param_4 = obj->getMin();
*/
剑指 Offer 31. 栈的压入、弹出序列
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如,序列 {1,2,3,4,5} 是某栈的压栈序列,序列 {4,5,3,2,1} 是该压栈序列对应的一个弹出序列,但 {4,3,5,1,2} 就不可能是该压栈序列的弹出序列。
示例 1:
输入:pushed = [1,2,3,4,5], popped = [4,5,3,2,1]
输出:true
解释:我们可以按以下顺序执行:
push(1), push(2), push(3), push(4), pop() -> 4,
push(5), pop() -> 5, pop() -> 3, pop() -> 2, pop() -> 1
示例 2:
输入:pushed = [1,2,3,4,5], popped = [4,3,5,1,2]
输出:false
解释:1 不能在 2 之前弹出。
思路:首先建立一个栈,我们将pushed中的元素进行入栈,判断栈中的top()和popped中的元素是否相等,如果相等那么就st.pop(),popped中的元素向后走一位,之后再将pushed中的下一个元素继续入栈;如果不相等就将push元素后移,循环结束条件为i<push.size(),此时pushed数组已经走到了结尾,最终只需要判断popped数组是否走到了结尾,要是两者都走到了结尾,就说明两者为栈的压入与弹出,不相等说明不是栈的压入与弹出
class Solution {
public:
bool validateStackSequences(vector<int>& pushed, vector<int>& popped) {
int i=0;
int j=0;
//建立一个栈
stack<int>st;
//循环结束条件:pushed数组遍历完
while(i<pushed.size())
{
//每次将pushed[i]进入栈中
st.push(pushed[i]);
//判断刚进入栈的元素,即栈顶元素是否与popped[j]相等,相等st.pop(),j++
while(!st.empty()&&st.top()==popped[j])
{
st.pop();
j++;
}
//直到st,top()与popped[j]不相等
之后++i;
i++;
}
//判断是否为栈的压入与弹出只需要判断popped数组是否走完了
return j==popped.size();
}
};
150. 逆波兰表达式求值
根据 逆波兰表示法,求表达式的值。
有效的算符包括 +、-、*、/ 。每个运算对象可以是整数,也可以是另一个逆波兰表达式。
说明:
整数除法只保留整数部分。
给定逆波兰表达式总是有效的。换句话说,表达式总会得出有效数值且不存在除数为 0 的情况。
示例 1:
输入:tokens = ["2","1","+","3","*"]
输出:9
解释:该算式转化为常见的中缀算术表达式为:((2 + 1) * 3) = 9
示例 2:
输入:tokens = ["4","13","5","/","+"]
输出:6
解释:该算式转化为常见的中缀算术表达式为:(4 + (13 / 5)) = 6
示例 3:
输入:tokens = ["10","6","9","3","+","-11","*","/","*","17","+","5","+"]
输出:22
解释:
该算式转化为常见的中缀算术表达式为:
((10 * (6 / ((9 + 3) * -11))) + 17) + 5
= ((10 * (6 / (12 * -11))) + 17) + 5
= ((10 * (6 / -132)) + 17) + 5
= ((10 * 0) + 17) + 5
= (0 + 17) + 5
= 17 + 5
= 22
提示:
1 <= tokens.length <= 104
tokens[i] 要么是一个算符("+"、"-"、"*" 或 "/"),要么是一个在范围 [-200, 200] 内的整数
逆波兰表达式:
逆波兰表达式是一种后缀表达式,所谓后缀就是指算符写在后面。
平常使用的算式则是一种中缀表达式,如 ( 1 + 2 ) * ( 3 + 4 ) 。
该算式的逆波兰表达式写法为 ( ( 1 2 + ) ( 3 4 + ) * ) 。
逆波兰表达式主要有以下两个优点:
去掉括号后表达式无歧义,上式即便写成 1 2 + 3 4 + * 也可以依据次序计算出正确结果。
适合用栈操作运算:遇到数字则入栈;遇到算符则取出栈顶两个数字进行计算,并将结果压入栈中。
逆波兰表达式(后缀表达式)求解方法:建立一个栈,遇到数字就入栈,遇到运算符就取出栈顶的两个元素进行相应的运算,并且将运算结果压入栈中
class Solution {
public:
//逆波兰表达式(后缀表达式)计算方法:遇到数字就入栈,遇到运算符就进行运算,运算完成后将结果再次返回到栈中,到最后返回栈顶元素即可
void GetNumber(stack<int>&st,int&left,int&right)
{
right=st.top();
st.pop();
left=st.top();
st.pop();
}
int evalRPN(vector<string>& tokens) {
stack<int>st;
//auto自动变量遍历每一个数组内容赋值到str中去
for(auto &str :tokens)
{
int left,right;
//str.back():返回末尾最后一位字符,用于判断它是什么符号
//遇到数字就入栈,遇到运算符就将栈顶两个元素拿出来进行运算,然后将运算结果继续入栈
switch(str.back())
{
case '+':
GetNumber(st,left,right);
st.push(left+right);
break;
case '-':
GetNumber(st,left,right);
st.push(left-right);
break;
case '*':
GetNumber(st,left,right);
st.push(left*right);
break;
case '/':
GetNumber(st,left,right);
st.push(left/right);
break;
default:
st.push(stoi(str));
}
}
//返回栈顶元素即为最终所求
return st.top();
}
};
此题目的难点:
1.switch语句中写str[0]到最后会导致出现负数的结果,这时我们换成str.back(),利用最后一个字母进行判断
2.将字符串转化为数字:stio(string)
3.注意将重复内容写出小函数
232. 用栈实现队列
请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作(push、pop、peek、empty):
实现 MyQueue 类:
void push(int x) 将元素 x 推到队列的末尾
int pop() 从队列的开头移除并返回元素
int peek() 返回队列开头的元素
boolean empty() 如果队列为空,返回 true ;否则,返回 false
说明:
你只能使用标准的栈操作 —— 也就是只有 push to top, peek/pop from top, size, 和 is empty 操作是合法的。
你所使用的语言也许不支持栈。你可以使用 list 或者 deque(双端队列)来模拟一个栈,只要是标准的栈操作即可。
进阶:
你能否实现每个操作均摊时间复杂度为 O(1) 的队列?换句话说,执行 n 个操作的总时间复杂度为 O(n) ,即使其中一个操作可能花费较长时间。
示例:
输入:
["MyQueue", "push", "push", "peek", "pop", "empty"]
[[], [1], [2], [], [], []]
输出:
[null, null, null, 1, 1, false]
解释:
MyQueue myQueue = new MyQueue();
myQueue.push(1); // queue is: [1]
myQueue.push(2); // queue is: [1, 2] (leftmost is front of the queue)
myQueue.peek(); // return 1
myQueue.pop(); // return 1, queue is [2]
myQueue.empty(); // return false
提示:
1 <= x <= 9
最多调用 100 次 push、pop、peek 和 empty
假设所有操作都是有效的 (例如,一个空的队列不会调用 pop 或者 peek 操作)
用战来实现队列:首先要明确栈和队列的特点,栈是先进后出的数据结构,而队列是一种先进先出的数据结构,当我们向栈中添加元素时,最先进入栈中的元素后排在栈底,这时我们定义两个栈,一个栈用来正常的录入数据,而另外一个栈用来辅助,我们将数据录入一个栈中,然后把这个栈反转到辅助栈中去,这时候原先栈低的元素就变成了栈顶的元素,这时候这个辅助栈栈顶的元素就相当于队列的队头元素,我们可以对这个元素进行各种操作,比如删除,就相当于我们删除队头元素,删除完之后我们又将该栈反转到原来栈中去,如此重复,就实现了用栈来实现队列
class MyQueue {
public:
//用栈来实现队列:
//建立两个栈,s1,s2,一个栈用来进数据,另一个栈用来出数据
/** Initialize your data structure here. */
stack<int>s1;//用来进数据
stack<int>s2;//用来出数据
MyQueue() {
}
/** Push element x to the back of queue. */
void push(int x) {
s1.push(x);
}
/** Removes the element from in front of queue and returns that element. */
int pop() {
while(!s1.empty())
{
s2.push(s1.top());
s1.pop();
}
int ret=s2.top();
s2.pop();
while(!s2.empty())
{
s1.push(s2.top());
s2.pop();
}
return ret;
}
/** Get the front element. */
int peek() {
while(!s1.empty())
{
s2.push(s1.top());
s1.pop();
}
int ret=s2.top();
while(!s2.empty())
{
s1.push(s2.top());
s2.pop();
}
return ret;
}
/** Returns whether the queue is empty. */
bool empty() {
return s1.empty()&&s2.empty();
}
};
225. 用队列实现栈
请你仅使用两个队列实现一个后入先出(LIFO)的栈,并支持普通栈的全部四种操作(push、top、pop 和 empty)。
实现 MyStack 类:
void push(int x) 将元素 x 压入栈顶。
int pop() 移除并返回栈顶元素。
int top() 返回栈顶元素。
boolean empty() 如果栈是空的,返回 true ;否则,返回 false 。
注意:
你只能使用队列的基本操作 —— 也就是 push to back、peek/pop from front、size 和 is empty 这些操作。
你所使用的语言也许不支持队列。 你可以使用 list (列表)或者 deque(双端队列)来模拟一个队列 , 只要是标准的队列操作即可。
示例:
输入:
["MyStack", "push", "push", "top", "pop", "empty"]
[[], [1], [2], [], [], []]
输出:
[null, null, null, 2, 2, false]
解释:
MyStack myStack = new MyStack();
myStack.push(1);
myStack.push(2);
myStack.top(); // 返回 2
myStack.pop(); // 返回 2
myStack.empty(); // 返回 False
提示:
1 <= x <= 9
最多调用100 次 push、pop、top 和 empty
每次调用 pop 和 top 都保证栈不为空
进阶:你能否实现每种操作的均摊时间复杂度为 O(1) 的栈?换句话说,执行 n 个操作的总时间复杂度 O(n) ,尽管其中某个操作可能需要比其他操作更长的时间。你可以使用两个以上的队列。
用队列实现栈:这就比前面那道题目稍微难一点了,队列是一种先进先出的数据结构,我们定义两个队列
我们始终用queue2来入数据,如了数据之后将queue1中的内容导入到queue1中去,然后交换两个队列的内容就能实现
1.queue1:
queue2:push(x)
1
·然后将queue1的值弄到queue2中去:
·queue1:
1
queue2:push(x)
2.queue1:
1
queue2:push(x)
2
然后将queue1中的内容导到queue2中去:
queue1:
queue2:push(x)
1 2
交换queue1与queue2的位置
queue1:
1 2
queue2:push(x)
3.queue 1:
1 2
queue2:push(x)
3
将queue1的内容全部导入到queue2中去
queue 1:
queue2:push(x)
1 2 3
交换queue1和queue2的位置:
queue 1:
1 2 3
queue2:push(x)
观察上面步骤我们可以看出来,如果我们这样操作的话,我们入队列时的顺序是1,2,3,不出以外的话出队列顺序也应该为1,2,3,而经过上述操作之后出队列顺序变成3,2,1,成功变成栈的操作了,这样我们的问题就变得简单多了
class MyStack {
public:
//使用队列来实现栈:
//队列:先进先出
//栈:先进后出
/** Initialize your data structure here. */
queue<int>q1;
queue<int>q2;
MyStack() {
}
/** Push element x onto stack. */
void push(int x) {
q2.push(x);
while(!q1.empty())
{
q2.push(q1.front());
q1.pop(以上是关于栈和队列刷题集合的主要内容,如果未能解决你的问题,请参考以下文章