news 2026/7/22 2:57:26

Java 手动实现栈 + 后缀表达式计算器(完整思路 + 代码)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Java 手动实现栈 + 后缀表达式计算器(完整思路 + 代码)

整体思路总览

一、需求拆解

  1. 手动实现栈(Stack):底层用数组存储,提供入栈、出栈、查看栈顶、判空、获取大小基础方法,不使用 Java 自带java.util.Stack
  2. 实现四则运算计算器,分两步核心算法:
    • 中缀表达式转后缀表达式(逆波兰表达式):解决运算符优先级、括号问题
    • 后缀表达式求值:借助自定义栈完成数值计算
  3. 支持:加减乘除+ - * /、括号()、多位数、运算优先级* / > + -

二、栈设计思路

栈是后进先出 (LIFO)线性结构:

  1. 底层:动态扩容数组(简化版固定数组,方便理解)
  2. 指针top:记录栈顶下标,top=-1代表空栈
  3. 核心方法:
    • push():入栈,top++,存入元素
    • pop():出栈,取出栈顶元素,top--,栈空抛异常
    • peek():查看栈顶不弹出
    • isEmpty():判断栈是否为空
    • size():返回栈内元素个数

三、中缀转后缀表达式思路(核心规则)

中缀:1+2*(3-4)人看懂;后缀:1 2 3 4 - * +计算机方便栈计算

  1. 遍历表达式每一个字符:
    1. 数字:直接拼接到后缀结果字符串(处理多位数,连续数字合并)
    2. 左括号(:直接压入运算符栈
    3. 右括号):循环弹出栈顶运算符到后缀,直到遇到左括号,弹出左括号丢弃
    4. 运算符+-*/
      • 栈不为空 且 栈顶不是左括号 且栈顶运算符优先级 ≥ 当前运算符
      • 持续弹出栈顶运算符到后缀,最后把当前运算符入栈
  2. 遍历结束后,把栈中剩余所有运算符依次弹出追加到后缀末尾

优先级规则:* / = 2+ - = 1,括号无优先级

四、后缀表达式求值思路(栈计算)

  1. 创建数值栈,遍历后缀表达式分割后的每一项:
    1. 数字:转为整数压入数值栈
    2. 运算符:弹出两个数(注意顺序:先弹右操作数,再弹左操作数)
      • 例:3 4 -,先出 4,再出 3,计算3-4
      • 计算结果重新压回栈
  2. 遍历完成,栈中仅剩一个元素,即为最终结果

完整 Java 代码

1. 自定义栈类(支持泛型,运算符、数字都能存)

java

运行

/** * 手动实现栈 LIFO * @param <T> 存储元素类型 */ public class MyStack<T> { // 底层数组存储 private Object[] arr; // 栈顶指针,-1代表空 private int top; // 初始容量 private static final int DEFAULT_CAPACITY = 10; public MyStack() { arr = new Object[DEFAULT_CAPACITY]; top = -1; } // 入栈 public void push(T val) { // 扩容判断 if (top == arr.length - 1) { grow(); } top++; arr[top] = val; } // 出栈,返回栈顶元素 @SuppressWarnings("unchecked") public T pop() { if (isEmpty()) { throw new RuntimeException("栈为空,无法出栈"); } T res = (T) arr[top]; arr[top] = null; // 清空引用 top--; return res; } // 查看栈顶,不弹出 @SuppressWarnings("unchecked") public T peek() { if (isEmpty()) { throw new RuntimeException("栈为空,无栈顶元素"); } return (T) arr[top]; } // 判断栈空 public boolean isEmpty() { return top == -1; } // 获取栈内元素数量 public int size() { return top + 1; } // 数组扩容 2倍 private void grow() { Object[] newArr = new Object[arr.length * 2]; System.arraycopy(arr, 0, newArr, 0, arr.length); arr = newArr; } }

2. 计算器工具类(中缀转后缀 + 后缀求值)

java

运行

public class Calculator { public static void main(String[] args) { // 测试用例 String expr1 = "1+2*(3-4)"; String expr2 = "10+20*3/5"; String expr3 = "(100-20)/8+9"; calc(expr1); calc(expr2); calc(expr3); } /** * 统一计算入口 * @param infix 中缀表达式 */ public static void calc(String infix) { System.out.println("======================"); System.out.println("中缀表达式:" + infix); String suffix = infixToSuffix(infix); System.out.println("后缀表达式:" + suffix); int result = calcSuffix(suffix); System.out.println("计算结果:" + result); } // 1. 获取运算符优先级 private static int getPriority(char op) { return switch (op) { case '+', '-' -> 1; case '*', '/' -> 2; default -> 0; // 括号 }; } // 2. 中缀表达式 -> 后缀表达式 private static String infixToSuffix(String infix) { // 运算符栈 MyStack<Character> opStack = new MyStack<>(); // 存储后缀表达式 StringBuilder suffix = new StringBuilder(); for (int i = 0; i < infix.length(); i++) { char ch = infix.charAt(i); // 情况1:数字,处理多位数 if (Character.isDigit(ch)) { // 连续数字拼接 while (i < infix.length() && Character.isDigit(infix.charAt(i))) { suffix.append(infix.charAt(i)); i++; } i--; // 回退,抵消外层i++ suffix.append(" "); // 空格分隔数字与运算符 } // 情况2:左括号 直接入栈 else if (ch == '(') { opStack.push(ch); } // 情况3:右括号 else if (ch == ')') { // 弹出直到左括号 while (!opStack.isEmpty() && opStack.peek() != '(') { suffix.append(opStack.pop()).append(" "); } opStack.pop(); // 弹出左括号丢弃 } // 情况4:四则运算符 +-*/ else if (ch == '+' || ch == '-' || ch == '*' || ch == '/') { // 栈顶优先级 >= 当前,持续弹出 while (!opStack.isEmpty() && opStack.peek() != '(' && getPriority(opStack.peek()) >= getPriority(ch)) { suffix.append(opStack.pop()).append(" "); } opStack.push(ch); } } // 遍历结束,弹出剩余所有运算符 while (!opStack.isEmpty()) { suffix.append(opStack.pop()).append(" "); } return suffix.toString().trim(); } // 3. 计算后缀表达式 private static int calcSuffix(String suffix) { MyStack<Integer> numStack = new MyStack<>(); // 按空格分割每一项 String[] items = suffix.split(" "); for (String item : items) { // 数字,入数值栈 if (item.length() == 1 && !Character.isDigit(item.charAt(0))) { // 运算符,弹出两个数计算 char op = item.charAt(0); int right = numStack.pop(); int left = numStack.pop(); int res = switch (op) { case '+' -> left + right; case '-' -> left - right; case '*' -> left * right; case '/' -> left / right; default -> throw new RuntimeException("非法运算符"); }; numStack.push(res); } else { // 数字转int入栈 int num = Integer.parseInt(item); numStack.push(num); } } return numStack.pop(); } }

运行输出示例

plaintext

====================== 中缀表达式:1+2*(3-4) 后缀表达式:1 2 3 4 - * + 计算结果:-1 ====================== 中缀表达式:10+20*3/5 后缀表达式:10 20 3 * 5 / + 计算结果:22 ====================== 中缀表达式:(100-20)/8+9 后缀表达式:100 20 - 8 / 9 + 计算结果:19

补充关键细节说明

1. 自定义栈关键点

  • 使用泛型MyStack<T>,既能存Character(运算符)又能存Integer(数字),复用一套栈逻辑
  • 内置数组自动扩容,避免栈溢出,符合真实栈设计
  • 边界校验:空栈 pop/peek 直接抛异常,防止数组下标越界

2. 多位数处理难点

普通单字符遍历无法识别10、100这类数字,遇到数字后循环向后读取连续数字,拼接完整数值,后缀中用空格分割数字和运算符,求值时方便分割。

3. 减法、除法顺序坑

后缀计算弹出顺序必须先右操作数,后左操作数: 比如5-3后缀5 3 -,先 pop 得到 3,再 pop 得到 5,5-3; 如果顺序颠倒会算出负数错误结果。

4. 括号处理逻辑

左括号只作为优先级分隔标记,遇到右括号持续出栈直到(,最后丢弃左括号,不会进入后缀表达式参与计算。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/7/22 2:57:15

自建代码仓库:手把手用 Gitea 搭一个自己的 Git 服务器

每个开发者电脑里都有那么几个"见不得人"的项目&#xff1a;写到一半的练手代码、存着各种私人配置的 dotfiles、不想公开但又想在多台电脑间同步的笔记仓库。扔到公共代码托管平台吧&#xff0c;私有仓库总有点不自在&#xff1b;只在本地放着吧&#xff0c;换台电脑…

作者头像 李华
网站建设 2026/7/22 2:57:14

JSON与JSONPATH:数据查询与处理核心技术解析

1. JSON与JSONPATH基础解析JSON&#xff08;JavaScript Object Notation&#xff09;作为轻量级数据交换格式&#xff0c;已经成为现代Web开发和API设计的标配。我第一次接触JSON是在2012年开发电商平台接口时&#xff0c;当时XML还是主流&#xff0c;但JSON简洁的键值对结构和…

作者头像 李华
网站建设 2026/7/22 2:55:57

ASP.NET Core集成Swagger实现高效API文档管理

1. 项目概述&#xff1a;为什么API文档如此重要&#xff1f;在开发现代Web API时&#xff0c;良好的文档就像城市中的路标系统。想象一下&#xff0c;你开发了一个功能强大的API&#xff0c;但其他开发者却不知道如何调用它——这就像建造了一座没有出口标识的迷宫。ASP.NET Co…

作者头像 李华
网站建设 2026/7/22 2:55:31

Unity集成Chord实现实时视频内容识别:本地AI驱动的游戏交互新范式

1. 项目概述&#xff1a;当游戏遇见“看懂”视频的AI最近在做一个挺有意思的Unity项目&#xff0c;核心需求是让游戏能“看懂”玩家摄像头里的实时画面。比如&#xff0c;玩家用手机对着客厅&#xff0c;游戏就能识别出电视里正在播放的足球比赛&#xff0c;并自动在游戏里生成…

作者头像 李华
网站建设 2026/7/22 2:54:28

计算机毕业设计之学生成绩管理系统

在各学校的教学过程中&#xff0c;学生的成绩管理是一项非常重要的事情。随着计算机多媒体技术的发展和网络的普及&#xff0c;“基于网络的学习模式”正悄无声息的改变着传统的成绩管理模式&#xff0c;学生成绩管理系统的研究和设计也成为教育技术领域的热点课题。采用当前流…

作者头像 李华
网站建设 2026/7/22 2:54:10

Python表达式求值原理与实现详解

1. 表达式求值的基本概念表达式求值是编程语言中最基础也最重要的功能之一。在Python中&#xff0c;表达式求值遵循从左到右的顺序&#xff0c;但在处理赋值操作时&#xff0c;右侧会先于左侧被求值。这种设计确保了表达式能够按照预期的算术优先级顺序进行计算。Python中的表达…

作者头像 李华