计算器
1.字符串转整数
string s = "458";int n = 0;
for (int i = 0; i < s.size(); i++) {char c = s[i];n = 10 * n + (c - '0');
}
// n 现在就等于 458
2.处理加减法
我们拿字符串算式1-12+3
为例,来说一个很简单的思路:
1、先给第一个数字加一个默认符号+
,变成+1-12+3
。
2、把一个运算符和数字组合成一对儿,也就是三对儿+1
,-12
,+3
,把它们转化成数字,然后放到一个栈中。
3、将栈中所有的数字求和,就是原算式的结果。
int calculate(string s) {stack<int> stk;// 记录算式中的数字int num = 0;// 记录 num 前的符号,初始化为 +char sign = '+';for (int i = 0; i < s.size(); i++) {char c = s[i];// 如果是数字,连续读取到 numif (isdigit(c)) num = 10 * num + (c - '0');// 如果不是数字,就是遇到了下一个符号,// 之前的数字和符号就要存进栈中if (!isdigit(c) || i == s.size() - 1) {switch (sign) {case '+':stk.push(num); break;case '-':stk.push(-num); break;}// 更新符号为当前符号,数字清零sign = c;num = 0;}}// 将栈中所有结果求和就是答案int res = 0;while (!stk.empty()) {res += stk.top();stk.pop();}return res;
}
i
就是从左到右扫描,sign
和num
跟在它身后。当s[i]
遇到一个运算符时,情况是这样的
所以说,此时要根据sign
的 case 不同选择nums
的正负号,存入栈中,然后更新sign
并清零nums
记录下一对儿符合和数字的组合。
另外注意,不只是遇到新的符号会触发入栈,当i
走到了算式的尽头(i == s.size() - 1
),也应该将前面的数字入栈,方便后续计算最终结果。
3. 处理乘除法
for (int i = 0; i < s.size(); i++) {char c = s[i];if (isdigit(c)) num = 10 * num + (c - '0');if (!isdigit(c) || i == s.size() - 1) {switch (sign) {int pre;case '+':stk.push(num); break;case '-':stk.push(-num); break;// 只要拿出前一个数字做对应运算即可case '*':pre = stk.top();stk.pop();stk.push(pre * num);break;case '/':pre = stk.top();stk.pop();stk.push(pre / num);break;}// 更新符号为当前符号,数字清零sign = c;num = 0;}
}
乘除法优先于加减法体现在,乘除法可以和栈顶的数结合,而加减法只能把自己放入栈。
4.处理括号
那么,为什么说处理括号没有看起来那么难呢,因为括号具有递归性质。我们拿字符串3*(4-5/2)-6
举例:
calculate(3*(4-5/2)-6
)
= 3 * calculate(4-5/2
) - 6
= 3 * 2 - 6
= 0
可以脑补一下,无论多少层括号嵌套,通过 calculate 函数递归调用自己,都可以将括号中的算式化简成一个数字。换句话说,括号包含的算式,我们直接视为一个数字就行了。
现在的问题是,递归的开始条件和结束条件是什么?遇到(
开始递归,遇到)
结束递归:python版本
def calculate(s: str) -> int:def helper(s: List) -> int:stack = []sign = '+'num = 0while len(s) > 0:c = s.pop(0)if c.isdigit():num = 10 * num + int(c)# 遇到左括号开始递归计算 numif c == '(':num = helper(s)if (not c.isdigit() and c != ' ') or len(s) == 0:if sign == '+': ...elif sign == '-': ... elif sign == '*': ...elif sign == '/': ...num = 0sign = c# 遇到右括号返回递归结果if c == ')': breakreturn sum(stack)return helper(list(s))
Java版本:224. 基本计算器
public int calculate(String s) {// Write your code hereDeque<Character> q = new LinkedList<>();for (char c : s.toCharArray()) {q.offer(c);}return dfs_cal(q);
}
// 得到括号内的结果
public int dfs_cal(Deque<Character> q) {Stack<Integer> stack = new Stack<>();char op = '+';int num = 0;while (!q.isEmpty()) {char c = q.pollFirst();if (c == '(') {num = dfs_cal(q);}if (Character.isDigit(c)) {num = num * 10 + c - '0';}// 读取到最后一位时 要处理最后的逻辑if (q.isEmpty() || (!Character.isDigit(c) && c != ' ')) {if (op == '+') {stack.push(num);} else if (op == '-') {stack.push(-num);} else if (op == '*') {stack.push(stack.pop() * num);} else if (op == '/') {stack.push(stack.pop() / num);}op = c;num = 0;}// 右括号判断只能在最后 前面要处理计算逻辑if (c == ')') {break;}}int res = 0;for (int t : stack) {res += t;}return res;
}
中文转阿拉伯
//中文字转阿拉伯数字
public long zn_int(String num){if (num == null || num.length() == 0) return 0L;HashMap<Character, Long> numMap = new HashMap<>();numMap.put('零', 0L);numMap.put('一', 1L);numMap.put('二', 2L);numMap.put('三', 3L);numMap.put('四', 4L);numMap.put('五', 5L);numMap.put('六', 6L);numMap.put('七', 7L);numMap.put('八', 8L);numMap.put('九', 9L);numMap.put('十', 10L);numMap.put('廿', 20L);numMap.put('卅', 30L);numMap.put('百', 100L);numMap.put('千', 1000L);numMap.put('万', 10_000L);numMap.put('亿', 100_000_000L);// year 2 long 年份判断 一九四五 这种long tmpres = 0; for (char c : num.toCharArray()) {long cur = numMap.get(c);if (cur >= 10) {tmpres = -1;break;}tmpres = tmpres*10 + cur;}if (tmpres > 0) return tmpres;LinkedList<Long> stack = new LinkedList<>();for (char c : num.toCharArray()) {long cur = numMap.get(c);if (stack.isEmpty() || stack.peek() > cur) {stack.push(cur);} else {long sum = 0;while (!stack.isEmpty() && stack.peek() < cur) {sum += stack.pop();}sum = (sum == 0 ? 1 : sum);stack.push(sum * cur);}}long res = 0;for (long t : stack){res += t;}return res;
}