栈和队列刷题集合

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 11     2
   queue2:push(x)
                              3
   将queue1的内容全部导入到queue2中去
   queue 1:
                      
   queue2:push(x)
                1     2          3
   交换queue1和queue2的位置:
      queue 11     2          3     
   queue2:push(x)
            
观察上面步骤我们可以看出来,如果我们这样操作的话,我们入队列时的顺序是123,不出以外的话出队列顺序也应该为123,而经过上述操作之后出队列顺序变成321,成功变成栈的操作了,这样我们的问题就变得简单多了
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(以上是关于栈和队列刷题集合的主要内容,如果未能解决你的问题,请参考以下文章

leetcode刷题记录——栈和队列

LeetCode通关:栈和队列六连,匹配问题有绝招

LeetCode通关:栈和队列六连,匹配问题有绝招

用LinkedList集合演示栈和队列的操作

Java集合与数据结构 栈和队列

Java集合与数据结构 栈和队列