栈常见问题

25 阅读4分钟

问题一: 有效的括号

问题描述

给定一个只包括 '('')''{''}''['']' 的字符串 s ,判断字符串是否有效。

有效字符串需满足:

  1. 左括号必须用相同类型的右括号闭合。
  2. 左括号必须以正确的顺序闭合。
  3. 每个右括号都有一个对应的相同类型的左括号。

 

示例 1:

输入: s = "()"

输出: true

示例 2:

输入: s = "()[]{}"

输出: true

示例 3:

输入: s = "(]"

输出: false

示例 4:

输入: s = "([])"

输出: true

代码

我们可以把左括号压入栈中,当第一次遇到右括号的时候与栈顶的左括号进行匹配,如果匹配说明是相同类型的

#include <iostream>
#include <stack>
#include <string>

using namespace std;

bool IsValid(string s) {
  stack<char> cs;

  for (char ch : s)
  {
    if (ch == '(' || ch == '{' || ch == '[') {
      // 左括号压栈
      cs.push(ch);
    } else {

      if (cs.empty()) {
        // 当匹配右括号时候发现栈中没有左括号
        return false;
      }

      // 遇到右括号
      char cmp = cs.top(); // 获取栈顶元素
      cs.pop(); 

      if (ch == ')' && cmp != '('
          || ch == ']' && cmp != '['
          || ch == '}' && cmp != '{'
      ) {
        // 没有匹配上
        return false;
      }
    }
  }

  // 出了循环栈里面的括号要取完
  if (!cs.empty()) {
    return false;
  }

  return true;
  
}

int main() {
  // 匹配的情况
  string validStr = "[({({})})]";
  cout << IsValid(validStr) << endl;

  // 不匹配的情况
  string invalidStr = "[({({})})";
  cout << IsValid(invalidStr) << endl;

  return 0;
}

测试

➜  build git:(main) ✗ ./stack1       
1
0

问题二:逆波兰表达式(后缀表达式)

问题描述

给你一个字符串数组 tokens ,表示一个根据 逆波兰表示法 表示的算术表达式。

请你计算该表达式。返回一个表示表达式值的整数。

注意:

  • 有效的算符为 '+''-''*' 和 '/' 。
  • 每个操作数(运算对象)都可以是一个整数或者另一个表达式。
  • 两个整数之间的除法总是 向零截断 。
  • 表达式中不含除零运算。
  • 输入是一个根据逆波兰表示法表示的算术表达式。
  • 答案及所有中间计算结果可以用 32 位 整数表示。

 

示例 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

代码

遇到数字则入栈;遇到算符则取出栈顶两个数字进行计算,并将结果压入栈中

#include <iostream>
#include <stack>
#include <string>
#include <vector>

using namespace std;

int calc(int left, int right, char sign) {
  switch (sign)
  {
  case '+':
    return left + right;
    break;
  case '-':
    return left - right;
    break;
  case '*':
    return left * right;
    break;
  case '/':
    return left / right;
    break;
  }

  throw("error sign");

}

int evalRPN(vector<string>& tokens) {
  stack<int> intStack;

  for (string &str: tokens) {
    if (str.size() == 1 && (
      str[0] == '+' || str[0] == '-' || str[0] == '*' || str[0] == '/'
    )) {
      // 遇到运算符了,开始运算

      // 取出数据
      int right = intStack.top();
      intStack.pop();

      int left = intStack.top();
      intStack.pop();

      // 计算结果入栈
      int res = calc(left, right, str[0]);

      intStack.push(res);

    } else {
      // 遇到数字入栈
      intStack.push(stoi(str));


    }
  }

  // 最后的计算结果保存在栈中
  return intStack.top();
}

int main() {
  vector<string> tokens = {"2", "1", "+", "3", "*"};

  cout << evalRPN(tokens) << endl;

  return 0;
}

测试

➜  build git:(main) ✗ ./evalRPN      
9

问题三:中缀表达式转后缀表达式

主要思路是用一个栈保存符号

遇到符号

  • 栈为空,直接入栈
  • 如果是(,直接入栈
  • 用当前符号和栈顶符号比较优先级。如果当前符号优先级大于栈顶符号,直接入栈。如果当前符号优先级小于栈顶符号,栈顶符号出栈并输出,继续跟栈顶符号进行比较。如果遇到右括号),要一直将符号出栈直到从栈中弹出左括号为止。

代码

#include <iostream>
#include <stack>
#include <string>

using namespace std;

// 比较符号优先级
bool Priority(char ch, char topch) {
  if ((ch == '*' || ch == '/') && (topch == '+' || topch == '-')) {
    // 入栈
    return true;
  }

  // 需要在topch == '('之前判断
  if (ch == ')') {

    return false;
  }

  if (topch == '(') {
    // 栈顶符号如果为左括号,直接入栈
    return true;
  }

  return false;

}

string MiddleToEndExpr(string expr) {


  string result;  // 后缀表达式
  stack<char> s;

  for (char ch : expr) {
    if (ch > '0' && ch < '9') {
      // 遇到数字,直接输出
      result.push_back(ch);
    } else {
      // 处理符号

      for(;;) {
        if (s.empty() || ch == '(') {
          s.push(ch);
          break;
        }

        // 比较当前符号ch和栈顶符号的优先级
        char topch = s.top();

        // Priority如果为true: ch > topch
        if (Priority(ch, topch)) {
          // ch优先级高,直接入栈
          s.push(ch);
          break;  // 遇到优先级高的ch。,跳出循环
        } else {
          s.pop();
          
          if (topch == '(') break;  // 如果遇到右括号),一直出栈,直到左括号(

          result.push_back(topch);
        }
      }

      
      
    }
  }

  // 如果符号栈还存留符号,直接输出到后缀表达式中
  while (!s.empty())
  {
    result.push_back(s.top());
    s.pop();
  }
  

  return result;

}


int main() {

  cout << MiddleToEndExpr("(1+2)*(3-1)") << endl;


  return 0;
}

测试

➜  build git:(main) ✗ ./convert      
12+31-*