问题一: 有效的括号
问题描述
给定一个只包括 '('
,')'
,'{'
,'}'
,'['
,']'
的字符串 s
,判断字符串是否有效。
有效字符串需满足:
- 左括号必须用相同类型的右括号闭合。
- 左括号必须以正确的顺序闭合。
- 每个右括号都有一个对应的相同类型的左括号。
示例 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-*