青训营X豆包MarsCode 技术训练营第二课 | 豆包MarsCode AI 刷题
在编程的学习和实践中,实现一个基本计算器是一个里程碑式的项目。它不仅考验我们对基础语法的掌握,还挑战我们对算法和数据结构的理解。本文将深入探讨如何从零开始构建一个基本计算器,以及在这个过程中的思考和学习。
问题描述
我们的目标是实现一个计算器,它能够解析和计算包含基本算术运算符(加、减、乘、除)和括号的字符串表达式。这个任务要求我们处理不同的数据类型(数字和字符),并实现一个能够理解运算优先级的解析器。
问题分析
这个问题的关键在于如何有效地处理运算符的优先级和括号。在没有使用内置的eval函数的情况下,我们需要自己实现一个解析算法。栈数据结构在这里扮演了核心角色,因为它能够帮助我们管理运算符的顺序和优先级。
算法选择
我们选择了两个栈的算法:一个用于存储操作数(值栈),另一个用于存储操作符(运算符栈)。这种算法被称为“逆波兰表示法”或“后缀表示法”,它允许我们通过栈来简化计算过程。
代码实现
以下是Java语言实现的核心代码:
import java.util.*;
public class Main {
private static int precedence(char op) {
switch (op) {
case '+': case '-': return 1;
case '*': case '/': return 2;
default: return 0;
}
}
private static int applyOp(int a, int b, char op) {
switch (op) {
case '+': return a + b;
case '-': return a - b;
case '*': return a * b;
case '/': return a / b;
}
return 0;
}
public static int solution(String tokens) {
Stack<Integer> values = new Stack<>();
Stack<Character> ops = new Stack<>();
for (int i = 0; i < tokens.length(); i++) {
char c = tokens.charAt(i);
if (Character.isDigit(c)) {
int val = 0;
while (i < tokens.length() && Character.isDigit(tokens.charAt(i))) {
val = val * 10 + (c - '0');
i++;
}
i--;
values.push(val);
} else if (c == '(') {
ops.push(c);
} else if (c == ')') {
while (!ops.isEmpty() && ops.peek() != '(') {
values.push(applyOp(values.pop(), values.pop(), ops.pop()));
}
ops.pop();
} else {
while (!ops.isEmpty() && precedence(ops.peek()) >= precedence(c)) {
values.push(applyOp(values.pop(), values.pop(), ops.pop()));
}
ops.push(c);
}
}
while (!ops.isEmpty()) {
values.push(applyOp(values.pop(), values.pop(), ops.pop()));
}
return values.pop();
}
public static void main(String[] args) {
System.out.println(solution("3+4*5/(3+2)")); // 输出 7
}
}
个人思考
在实现计算器的过程中,我深刻体会到了算法的力量。每一个决策,无论是选择数据结构还是设计算法流程,都直接影响了程序的性能和准确性。我也认识到了测试的重要性,因为只有通过全面的测试,我们才能确保我们的计算器能够处理各种复杂的情况。
此外,我也意识到了代码的可读性和可维护性。良好的代码结构和注释能够帮助他人(或未来的自己)更好地理解代码的意图和逻辑。
结语
通过构建一个基本计算器,我们不仅能够提升编程技能,还能加深对算法和数据结构的理解。希望这篇文章能够帮助你更好地理解如何实现一个基本计算器,并在编程学习中取得进步。通过不断的实践和学习,我们可以在技术领域不断成长和进步。