嵌套类递归
基础计算器 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);
}
394. 字符串解码
- 含有嵌套的字符串解码,时间复杂度 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";// abcacctacctkkxyzstring 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;}
};
726. 原子的数量
- 含有嵌套的分子式求原子数量,时间复杂度 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) {// 有序 mapmap<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;}
};