嵌套类递归
基础计算器 III
#include <iostream>
#include <string>
#include <vector>
using namespace std;
class Solution {
public:
int where;
int calculate(string &s) {
where = 0;
return recursive(s, 0);
}
// 计算 s 中从 i 位置开始到他对应的右括号结束的位置
int recursive(string &s, int i) {
int cur = 0;
// 数字栈
vector<int> numbers;
// 符号栈
vector<char> ops;
while (i < s.length()) {
if (s[i] >= '0' && s[i] <= '9') {
// 解析数字
cur = cur * 10 + s[i++] - '0';
} else if (s[i] == '(') {
// 遇到左括号,递归处理下一段表达式
cur = recursive(s, i + 1);
// 从 where + 1 的位置继续解析
i = where + 1;
} else if (s[i] == ')') {
// 遇到右括号就退出循环
break;
} else {
// 遇到运算符,把解析好的数字和这个运算符入栈,cur 清零
push(numbers, ops, cur, s[i++]);
cur = 0;
}
}
// 最后一个数字,由于遇不到下一个运算符,所以要手动入栈
push(numbers, ops, cur, '+');
// 已经处理到的位置
where = i;
// 计算这一对括号内的表达式
return compute(numbers, ops);
}
void push(vector<int> &numbers, vector<char> &ops, int cur, char op) {
int n = numbers.size();
if (n == 0 || ops.back() == '+' || ops.back() == '-') {
// 栈空或者栈顶是加减运算符,就直接放入
numbers.push_back(cur);
ops.push_back(op);
} else {
// 栈顶是乘除运算,先弹出一个数字运算后,把结果入栈
int topNumber = numbers.back();
char topOp = ops.back();
if (topOp == '*') {
numbers[n - 1] = topNumber * cur;
} else {
numbers[n - 1] = topNumber / cur;
}
ops[n - 1] = op;
}
}
// 计算剩下的加减运算
int compute(const vector<int> &numbers, const vector<char> &ops) {
int n = numbers.size();
int ans = numbers[0];
for (int i = 1; i < n; i++)
ans += ops[i - 1] == '+' ? numbers[i] : -numbers[i];
return ans;
}
};
int main() {
string s = "6-4/2*(3-1)+(2+1)*(5-3*2+(4/2+2))";
Solution solution;
cout << solution.calculate(s);
}
- 递归(推荐),index++ 跳过左括号,而不是用 where + 1 去跳过
#include <iostream>
#include <string>
#include <vector>
using namespace std;
class Solution {
public:
// 处理到的位置
int index;
int calculate(string &s) {
return recursive(s);
}
// 计算 s 中从 index 位置开始到他对应的右括号结束的位置
int recursive(string &s) {
int cur = 0;
// 数字栈
vector<int> numbers;
// 符号栈
vector<char> ops;
for (; index < s.length(); index++) {
if (s[index] >= '0' && s[index] <= '9') {
// 解析数字
cur = cur * 10 + s[index] - '0';
} else if (s[index] == '(') {
// 跳过左括号
index++;
// 递归处理下一段表达式
cur = recursive(s);
} else if (s[index] == ')') {
// 遇到右括号就退出循环
break;
} else {
// 遇到运算符,把解析好的数字和这个运算符入栈,cur 清零
push(numbers, ops, cur, s[index]);
cur = 0;
}
}
// 最后一个数字,由于遇不到下一个运算符,所以要手动入栈
push(numbers, ops, cur, '+');
// 计算这一对括号内的表达式
return compute(numbers, ops);
}
void push(vector<int> &numbers, vector<char> &ops, int cur, char op) {
int n = numbers.size();
if (n == 0 || ops.back() == '+' || ops.back() == '-') {
// 栈空或者栈顶是加减运算符,就直接放入
numbers.push_back(cur);
ops.push_back(op);
} else {
// 栈顶是乘除运算,先弹出一个数字运算后,把结果入栈
int topNumber = numbers.back();
char topOp = ops.back();
if (topOp == '*') {
numbers[n - 1] = topNumber * cur;
} else {
numbers[n - 1] = topNumber / cur;
}
ops[n - 1] = op;
}
}
// 计算剩下的加减运算
int compute(const vector<int> &numbers, const vector<char> &ops) {
int n = numbers.size();
int ans = numbers[0];
for (int j = 1; j < n; j++)
ans += ops[j - 1] == '+' ? numbers[j] : -numbers[j];
return ans;
}
};
int main() {
string s = "6-4/2*(3-1)+(2+1)*(5-3*2+(4/2+2))";
Solution solution;
cout << solution.calculate(s);
}
- 含有嵌套的字符串解码,时间复杂度 O(n)
- 从第一个右括号开始解析,从里向外解析每一对括号,解析成字符串后入栈,继续找下个右括号
#include <iostream>
#include <string>
#include <vector>
#include <stack>
using namespace std;
class Solution {
public:
string decodeString(string s) {
stack<char> stk;
// 用于逆序输出栈中内容
deque<char> deq;
for (int i = 0; i < s.size(); ++i) {
// 右括号前的全部入栈
while (i < s.size() && s[i] != ']') {
stk.emplace(s[i]);
i++;
}
// 已经到字符串 s 的结尾了
if (s[i] != ']') break;
// 解析括号内的字符串
string tempStr;
while (stk.top() != '[') {
deq.emplace_front(stk.top());
stk.pop();
}
while (!deq.empty()) {
tempStr += deq.front();
deq.pop_front();
}
// 弹出 '['
stk.pop();
// 解析左括号前的数字,也就是括号内字符的重复次数
int count = 0;
while (!stk.empty() && stk.top() >= '0' && stk.top() <= '9') {
deq.emplace_front(stk.top());
stk.pop();
}
while (!deq.empty()) {
count *= 10;
count += deq.front() - '0';
deq.pop_front();
}
// 记录重复 count 次的字符串
string repeated;
for (int j = 0; j < count; ++j)
repeated.append(tempStr);
// 重新入栈
for (auto &c: repeated)
stk.emplace(c);
}
// 全部出栈
string res;
deq.clear();
while (!stk.empty()) {
deq.emplace_front(stk.top());
stk.pop();
}
while (!deq.empty()) {
res += deq.front();
deq.pop_front();
}
return res;
}
};
- 用两个栈分别记录左括号之前的数字,以及数字前的字符串。每次遇到右括号就把括号内重复相应次数,然后与这个数字之前的字符串拼接
class Solution {
public:
// "abc2[a2[c]t]2[k]xyz";
// abcacctacctkkxyz
string decodeString(const string &s) {
// 临时记录数字
int multi = 0;
// 临时记录数字前的字符串
string str;
stack<int> stack_multi;
stack<string> stack_str;
for (char c: s) {
if (c == '[') {
// 遇到左括号,说明左括号前的数字已经被确定,存入栈中
stack_multi.emplace(multi);
// 数字之前的字符串也确定了,存入栈中
stack_str.emplace(str);
// 清空这两个临时变量
multi = 0;
str.clear();
} else if (c == ']') {
// 取出当前括号内字符串应该重复的次数
int cur_multi = stack_multi.top();
stack_multi.pop();
// 重复对应的次数后记录到tmp中
string tmp;
for (int i = 0; i < cur_multi; i++) tmp += str;
// 再接到之前数字前已经出现的字符串后面
str = stack_str.top() + tmp;
stack_str.pop();
} else if (c >= '0' && c <= '9') {
// 确定重复次数
multi = multi * 10 + (c - '0');
} else {
// 记录数字前的字符串,或者是括号内的字符串
str.push_back(c);
}
}
return str;
}
};
#include <string>
using namespace std;
class Solution {
public:
int where;
string recursive(string s, int i) {
string str;
int multi = 0;
while (i < s.length() && s[i] != ']') {
if (s[i] >= '0' && s[i] <= '9') {
// 解析重复次数
multi = 10 * multi + s[i++] - '0';
} else if (s[i] == '[') {
// 递归处理下一段
string repeatedStr = recursive(s, i + 1);
// 重复 multi 次,并且接在 str 后面
while (multi > 0) {
str += repeatedStr;
multi--;
}
i = where + 1;
} else {
// 解析数字前的字符串,或者括号内的字符串
str += s[i++];
}
}
where = i;
return str;
}
string decodeString(string s) {
where = 0;
return recursive(s, 0);
}
};
- 递归(推荐),index++ 跳过左括号,而不是用 where + 1 去跳过
#include <string>
using namespace std;
class Solution {
public:
// 当前处理位置的下标
int index = 0;
string decodeString(string s) {
string str;
int multi = 0;
for (; index < s.size(); index++) {
if (s[index] >= '0' && s[index] <= '9') {
// 解析重复次数
multi = 10 * multi + s[index] - '0';
} else if (s[index] == '[') {
// 跳过这个左括号
index++;
// 递归处理括号内的字符串
string repeatedStr = decodeString(s);
// 重复 multi 次,并且接在 str 后面
while (multi > 0) {
str += repeatedStr;
multi--;
}
} else if (s[index] == ']') {
// 结束递归,返回括号内的字符串
break;
} else {
// 解析数字前的字符串,或者括号内的字符串
str += s[index];
}
}
return str;
}
};
- 含有嵌套的分子式求原子数量,时间复杂度 O(n)
- 处理过程
- 遇到字母:解析出元素符号,以及该元素出现次数
- 遇到左括号
(
:递归处理这段括号的分子式,从左括号一直到右括号后面的数字
- 遇到右括号
)
:如果右括号后面是数字,则需要乘对应倍数
#include <string>
#include <iostream>
#include <map>
using namespace std;
class Solution {
public:
// 分子式中处理到的下标
int index;
bool isNumber(char ch) {
return ch >= '0' && ch <= '9';
}
bool isLowercaseLetter(char ch) {
return ch >= 'a' && ch <= 'z';
}
bool isUppercaseLetter(char ch) {
return ch >= 'A' && ch <= 'Z';
}
// 返回原子出现次数
map<string, int> recursive(string &formula) {
// 有序 map
map<string, int> freq;
while (index < formula.length()) {
if (isUppercaseLetter(formula[index])) {
// 化学元素符号由一个大写字母或者一个大写字母加若干个小写字母确定
string atom = "";
atom += formula[index++];
// 先生成完整的元素符号,如果后面是小写字母就追加上去
while (index < formula.length() && isLowercaseLetter(formula[index]))
atom += formula[index++];
// 判断这个元素符号出现的次数
int cnt = 0;
if (index < formula.length() && isNumber(formula[index])) {
while (index < formula.length() && isNumber(formula[index]))
cnt = cnt * 10 + formula[index++] - '0';
} else {
// 只出现一次
cnt = 1;
}
freq[atom] += cnt;
} else if (formula[index] == '(') {
index++;
// 递归处理这段括号的分子式,从左括号一直到右括号后面的数字
map<string, int> tempCount = recursive(formula);
// 累加出现次数
for (const auto &item: tempCount)
freq[item.first] += item.second;
// 结束后 index 为下一个非数字的字符的下标,继续从 index 开始处理
} else if (formula[index] == ')') {
index++;
// 如果右括号后面是数字,则需要乘对应倍数
if (index < formula.length() && isNumber(formula[index])) {
int cnt = 0;
while (index < formula.length() && isNumber(formula[index]))
cnt = cnt * 10 + formula[index++] - '0';
for (auto &item: freq)
freq[item.first] = item.second * cnt;
}
// 返回这一段分子式的元素统计
return freq;
}
}
return freq;
}
string countOfAtoms(string formula) {
index = 0;
map<string, int> count = recursive(formula);
string res;
// 从有序 map 中生成结果字符串
for (const auto &item: count) {
res.append(item.first);
if (item.second != 1)
res.append(to_string(item.second));
}
return res;
}
};