中缀表达式计算器——Java实现,有简单图形界面

中缀表达式计算器——Java实现,有简单图形界面

前言:

本篇文章撰写的初衷是为了记录本人的学习以及便于本人的再次学习,并为感兴趣的读者提供一个简单的思路参考。由于本人的能力有限,所以出现错误之处尽请见谅,如若能指出错误之处,笔者不胜感激。

问题描述:

给定一个中缀表达式,将中缀表达式转换为后缀表达式(逆波兰表达式),并计算出逆波兰表达式的值。

本逆波兰计算器限制:
  1. 支持多位整数,不支持小数(对于支持小数的代码编写有兴趣的读者可以自行思考)
  2. 支持操作符: ‘+’, ‘-’, ‘*’, ‘/’, ‘(’, ‘)’
逆波兰计算器算法:

一、中缀表达式转换为后缀表达式(逆波兰表达式)算法:

  1. 初始化两个栈,一个为存储操作符的操作符栈,一个为存储逆波兰表达式的逆波兰表达式栈。
  2. 从左至右扫描中缀表达式。
  3. 若读取到的字符为数字,则向后分析直到数字串的结束,并将数字串压入逆波兰表达式栈。
  4. 若读取到的字符为’(’,则直接将’(‘压入操作符栈,该操作符只有遇到右括号’)'的时候出栈。
  5. 若读取到的字符为’)’,则将操作符栈中的栈顶操作符依次出栈并压入逆波兰表达式栈,直到遇到左括号’(‘为止,将操作符栈中栈顶的左括号’('出栈。
  6. 若读取到的字符为运算符’+’, ‘-’, ‘*’, ‘/’:
    a. 如果操作符栈为空或栈顶元素为’(’,则将读取到运算符直接压入操作符栈;
    b. 如果读取的运算符的优先级高于操作符栈栈顶运算符的优先级,则将读取的运算符直接压入操作符栈;
    c. 如果读取的运算符的优先级低于或等于操作符栈栈顶运算符的优先级,则将操作符栈栈顶运算符出栈并压入逆波兰表达式栈,将读取到的运算符压入操作符栈。(注意,此处优先级的比较是不断比较操作符栈栈顶运算符的优先级直到读取到的运算符的优先级高于操作符栈栈顶运算符的优先级或遇到’('或栈空)。
  7. 重复2-6操作直到输入的中缀表达式扫描完毕,若操作符栈中仍然存在运算符,则将操作符栈中栈顶的运算符依次出栈并压入逆波兰表达式栈直到操作符栈为空。
  8. 逆波兰表达式栈的出栈元素的逆序为中缀表达式转换后的后缀表达式(逆波兰表达式)。

二、后缀表达式(逆波兰表达式)求值算法:

  1. 初始化一个操作数栈。
  2. 从左至右依次扫描后缀表达式的单元。
  3. 如果扫描的单元是数字串,则将其转换为数字,并压入操作数栈,并扫描下一个单元。
  4. 如果扫描的单元是运算符,则对操作数栈栈顶上的两个操作数执行该运算(取两次栈顶),并将运算结果重新压入操作数栈。
  5. 重复2-4操作,直到后缀表达式单元扫描完毕,最终操作数栈中栈顶元素即为计算结果值。
测试举例:

输入中缀表达式:10+((20+30)*40)-50 (注意输入为英文字符)

求得后缀表达式:10 20 30 + 40 * + 50 - (中间用空格隔开)

后缀表达式计算结果:1960

中缀表达式转后缀表达式过程分析:

扫描到的字符操作符栈(栈底->栈顶)逆波兰表达式栈(栈底->栈顶)说明
1扫描到为数字,向后扫描直到数字串的结尾
010扫描到为数字串结尾,数字串压入逆波兰表达式栈
++10操作符栈为空,运算符直接压入操作符栈
(+,(10扫描到左括号’(’,直接压入操作符栈
(+,(,(10扫描到左括号’(’,直接压入操作符栈
2+,(,(10扫描到为数字,向后扫描直到数字串的结尾
0+,(,(10,20扫描到为数字串结尾,数字串压入逆波兰表达式栈
++,(,(,+10,20操作符栈栈顶为左括号’(’,运算符直接压入操作符栈
3+,(,(,+10,20扫描到为数字,向后扫描直到数字串的结尾
0+,(,(,+10,20,30扫描到为数字串结尾,数字串压入逆波兰表达式栈
)+,(10,20,30,+扫描到右括号’)’,弹出操作符栈栈顶运算符并压入逆波兰表达式栈直至遇到左括号,左括号出栈
*+,(,*10,20,30,+操作符栈栈顶为左括号’(’,运算符直接压入操作符栈
4+,(,*10,20,30,+扫描到为数字,向后扫描直到数字串的结尾
0+,(,*10,20,30,+,40扫描到为数字串结尾,数字串压入逆波兰表达式栈
)+10,20,30,+,40,*扫描到右括号’)’,弹出操作符栈栈顶运算符并压入逆波兰表达式栈直至遇到左括号,左括号出栈
--10,20,30,+,40,*,+扫描到的’-‘运算符与运算符栈的栈顶运算符’+‘优先级相同,故将栈顶运算符出栈并压入逆波兰表达式栈中,因为此时操作符栈已为空,故直接将扫描到的’-'压入操作符栈
5-10,20,30,+,40,*,+扫描到为数字,向后扫描直到数字串的结尾
0-10,20,30,+,40,*,+,50扫描到为数字串结尾,数字串压入逆波兰表达式栈
中缀表达式扫描完毕10,20,30,+,40,*,+,50,-将操作符栈中的运算符弹出并压入逆波兰表达式栈

后缀表达式的计算分析过程:

扫描到的单元操作数栈(栈底->栈顶)说明
1010扫描到的单元是数字串,则将其转换为数字,并压入操作数栈
2010,20扫描到的单元是数字串,则将其转换为数字,并压入操作数栈
3010,20,30扫描到的单元是数字串,则将其转换为数字,并压入操作数栈
+10,50扫描到的单元是运算符,则对操作数栈栈顶上的两个操作数执行该运算(取两次栈顶)20+30,并 将运算结果50重新压入操作数栈。
4010,50,40扫描到的单元是数字串,则将其转换为数字,并压入操作数栈
*10,2000扫描到的单元是运算符,则对操作数栈栈顶上的两个操作数执行该运算(取两次栈顶)50*40,并 将运算结果2000重新压入操作数栈。
+2010扫描到的单元是运算符,则对操作数栈栈顶上的两个操作数执行该运算(取两次栈顶)10+2000,并 将运算结果2010重新压入操作数栈。
502010,50扫描到的单元是数字串,则将其转换为数字,并压入操作数栈
-1960扫描到的单元是运算符,则对操作数栈栈顶上的两个操作数执行该运算(取两次栈顶)2010-50,并 将运算结果1960重新压入操作数栈。
后缀表达式扫描完毕1960后缀表达式单元扫描完毕,最终操作数栈中栈顶元素即为计算结果值
代码运行截图:

运行截图

Java源代码:
import java.awt.Font;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;

import javax.swing.BorderFactory;
import javax.swing.JButton;
import javax.swing.JFrame;
import javax.swing.JLabel;
import javax.swing.JPanel;
import javax.swing.JTextField;

/**
 * 逆波兰式计算器
 * 
 * @author 独孤猿1998
 *
 */
public class InversePolishCalculator extends JFrame {
	
	private static final long serialVersionUID = 1L;
	
	private JPanel backgroundPanel;
	private JLabel nifixExpressionLabel;
	private JTextField nifixExpressionTextField;
	private JButton calculateButton;
	private JButton clearButton;
	private JLabel postfixExpressionLabel;
	private JTextField postfixExpressionTextField;
	private JLabel calculateResultLabel;
	private JTextField calculateResultTextField;
	private static Font textFont = new Font("微软雅黑", Font.PLAIN, 18);
	private static Font buttonFont = new Font("微软雅黑", Font.PLAIN, 16);
	
	public InversePolishCalculator() {
		initialization();
	}
	
	/**
	 * 窗口初始化的方法
	 */
	public void initialization() {
		this.setTitle("逆波兰计算器");//设置窗口标题
		this.setSize(550, 320);//设置窗口大小
		this.setLocationRelativeTo(null);//设置窗口居中
		this.setResizable(false);//设置窗口大小不可改变
		
		this.setDefaultCloseOperation(EXIT_ON_CLOSE);
		addViewElement();//窗口添加组件
		this.setVisible(true);
	}
	
	/**
	 * 窗口添加组件的方法
	 */
	public void addViewElement() {
		backgroundPanel = new JPanel(null);
		nifixExpressionLabel = new JLabel("请输入中缀表达式:");
		nifixExpressionLabel.setSize(300, 20);
		nifixExpressionLabel.setLocation(40, 50);
		nifixExpressionLabel.setFont(textFont);
		backgroundPanel.add(nifixExpressionLabel);
		
		nifixExpressionTextField = new JTextField();
		nifixExpressionTextField.setSize(300, 30);
		nifixExpressionTextField.setLocation(40, 75);
		nifixExpressionTextField.setFont(textFont);
		backgroundPanel.add(nifixExpressionTextField);
		
		calculateButton = new JButton("计算");
		calculateButton.setSize(70, 30);
		calculateButton.setLocation(350, 75);
		calculateButton.setFont(buttonFont);
		calculateButton.setBorder(BorderFactory.createRaisedBevelBorder());
		calculateButton.addActionListener(new ActionListener() {
			
			@Override
			public void actionPerformed(ActionEvent e) {
				postfixExpressionTextField.setText(nifixTransformPostfix(nifixExpressionTextField.getText()));
				calculateResultTextField.setText("" + calculatePostfixExpression(postfixExpressionTextField.getText()));
			}
		});
		backgroundPanel.add(calculateButton);
		
		clearButton = new JButton("清空");
		clearButton.setSize(70, 30);
		clearButton.setLocation(430, 75);
		clearButton.setFont(buttonFont);
		clearButton.setBorder(BorderFactory.createRaisedBevelBorder());
		clearButton.addActionListener(new ActionListener() {
			
			@Override
			public void actionPerformed(ActionEvent e) {
				nifixExpressionTextField.setText("");
				postfixExpressionTextField.setText("");
				calculateResultTextField.setText("");
			}
		});
		backgroundPanel.add(clearButton);
		
		postfixExpressionLabel = new JLabel("中缀表达式转换后的后缀表达式:");
		postfixExpressionLabel.setSize(300, 20);
		postfixExpressionLabel.setLocation(40, 115);
		postfixExpressionLabel.setFont(textFont);
		backgroundPanel.add(postfixExpressionLabel);
		
		postfixExpressionTextField = new JTextField();
		postfixExpressionTextField.setSize(300, 30);
		postfixExpressionTextField.setLocation(40, 140);
		postfixExpressionTextField.setFont(textFont);
		backgroundPanel.add(postfixExpressionTextField);
		
		calculateResultLabel = new JLabel("计算结果:");
		calculateResultLabel.setSize(300, 20);
		calculateResultLabel.setLocation(40, 180);
		calculateResultLabel.setFont(textFont);
		backgroundPanel.add(calculateResultLabel);
		
		calculateResultTextField = new JTextField();
		calculateResultTextField.setSize(300, 30);
		calculateResultTextField.setLocation(40, 205);
		calculateResultTextField.setFont(textFont);
		backgroundPanel.add(calculateResultTextField);
		
		this.setContentPane(backgroundPanel);
	}
	
	/**
	 * 中缀表达式转换为后缀表达式的方法
	 * @param nifixExpression 中缀表达式
	 * @return 后缀表达式(逆波兰表达式)
	 */
	public static String nifixTransformPostfix(String nifixExpression) {
		Stack<Character> operatorStack = new Stack<Character>();//初始化存储操作符的操作符栈
		Stack<String> postfixExpressionStack = new Stack<String>();//初始化存储逆波兰表达式的逆波兰表达式栈
		int length = nifixExpression.length();//中缀表达式的字符串长度
		int index = 0;//用于指向扫描到中缀表达式的位置
		char element = ' ';//保存当前扫描到中缀表达式的字符
		String number = null;//用于存储多位数字
		while(index < length) {//从左至右扫描中缀表达式
			number = "";
			element = nifixExpression.charAt(index);
			if (element >= '0' && element <= '9') {//若读取到的字符为数字
				while(element >= '0' && element <= '9') {//则向后分析直到数字串的结束
					number = number + element;
					index++;
					if (index == length) {
						break;
					}
					element = nifixExpression.charAt(index);
				}
				postfixExpressionStack.push(number);//并将数字串压入逆波兰表达式栈
				continue;
			}else if (element == '(') {//若读取到的字符为'('
				operatorStack.push(element);//则直接将'('压入操作符栈
			}else if (element == ')') {//若读取到的字符为')'
				while (operatorStack.peek() != '(') {//则将操作符栈中的栈顶操作符依次出栈并压入逆波兰表达式栈,直到遇到左括号'('为止
					postfixExpressionStack.push(operatorStack.pop().toString());
				}
				operatorStack.pop();//将操作符栈中栈顶的左括号'('出栈
			}else if (element == '+' || element == '-' || element == '*' || element == '/') {//若读取到的字符为运算符'+', '-', '*', '/'
				if (operatorStack.isEmpty() || operatorStack.peek() == '(') {//如果操作符栈为空或栈顶元素为'('
					operatorStack.push(element);//则将读取到运算符直接压入操作符栈
				}else if (Operator.getOperatorPriority(element) > Operator.getOperatorPriority(operatorStack.peek())) {//如果读取的运算符的优先级高于操作符栈栈顶运算符的优先级
					operatorStack.push(element);//则将读取的运算符直接压入操作符栈
				}else {//如果读取的运算符的优先级低于或等于操作符栈栈顶运算符的优先级
					//则将操作符栈栈顶运算符出栈并压入逆波兰表达式栈(注意,此处优先级的比较是不断比较操作符栈栈顶运算符的优先级直到读取到的运算符的优先级高于操作符栈栈顶运算符的优先级或遇到'('或栈空)
					while(!operatorStack.isEmpty() && operatorStack.peek() != '(' && Operator.getOperatorPriority(element) <= Operator.getOperatorPriority(operatorStack.peek())) {
						postfixExpressionStack.push(operatorStack.pop().toString());
					}
					operatorStack.push(element);//将读取到的运算符压入操作符栈。
				}
			}else {
				throw new RuntimeException("扫描到未知字符!");
			}
			index++;
		}
		while(!operatorStack.isEmpty()) {//中缀表达式扫描完毕,若操作符栈中仍然存在运算符
			postfixExpressionStack.push(operatorStack.pop().toString());//则将操作符栈中栈顶的运算符依次出栈并压入逆波兰表达式栈直到操作符栈为空
		}
		//逆波兰表达式栈的出栈元素的逆序为中缀表达式转换后的后缀表达式(用空格隔开)
		String postfixExpression = postfixExpressionStack.pop();
		while(!postfixExpressionStack.isEmpty()) {
			postfixExpression = postfixExpressionStack.pop() + " " + postfixExpression;
		}
		return postfixExpression;
	}
	
	/**
	 * 计算后缀表达式结果的方法
	 * @param postfixExpression 后缀表达式(逆波兰表达式)
	 * @return 后缀表达式的计算结果
	 */
	public static int calculatePostfixExpression(String postfixExpression) {
		Stack<Integer> operandStack = new Stack<Integer>();//初始化一个操作数栈
		String[] elements = postfixExpression.split(" ");//将后缀表达式分解成一个个单元
		String element = null;//后缀表达式扫描到的单元
		char elementHead = ' ';
		int operand = 0, operand1 = 0, operand2 = 0;
		for (int i = 0; i < elements.length; i++) {//从左至右依次扫描后缀表达式的单元
			element = elements[i];
			elementHead = element.charAt(0);
			if (elementHead >= '0' && elementHead <= '9') {//如果扫描的单元是操作数串
				operand = Integer.parseInt(element);//则将其转换为数字
				operandStack.push(operand);//并压入操作数栈
			}else if(elementHead == '+' || elementHead == '-' || elementHead == '*' || elementHead == '/') {// 如果扫描的单元是一个运算符
				operand1 = operandStack.pop();//取操作数栈栈顶
				operand2 = operandStack.pop();//取操作数栈栈顶
				operand = calculateResult(operand1, operand2, elementHead);//则对操作数栈栈顶上的两个操作数执行该运算
				operandStack.push(operand);//将运算结果重新压入操作数栈
			}else {
				throw new RuntimeException("扫描到未知单元!");
			}
		}
		return operandStack.pop();
	}
	
	/**
	 * 用于计算给定操作数及操作符的结果的方法
	 * @param operand1
	 * @param operand2
	 * @param operator
	 * @return 计算结果
	 */
	public static int calculateResult(int operand1, int operand2, char operator) {
		int result = 0;
		switch (operator) {
		case '+':
			result = operand2 + operand1;
			break;
		case '-':
			result = operand2 - operand1;
			break;
		case '*':
			result = operand2 * operand1;
			break;
		case '/':
			result = operand2 / operand1;
			break;
		default:
			throw new RuntimeException("未定义运算符:" + operator);
		}
		return result;
	}
	
	public static void main(String[] args) {
		new InversePolishCalculator();
	}

}

/**
 * 运算符类,用于比较运算符的优先级
 */
class Operator {
	
	private static final int ADD = 1;//'+' 的优先级定义
	private static final int SUB = 1;//'-' 的优先级定义
	private static final int MUL = 2;//'*' 的优先级定义
	private static final int DIV = 2;//'/' 的优先级定义
	
	/**
	 * 获取给定运算符的优先级的犯法
	 * @param operator 给定的运算符
	 * @return 给定运算符的优先级
	 */
	public static int getOperatorPriority(char operator) {
		int priority = 0;
		switch (operator) {
		case '+':
			priority = ADD;
			break;
		case '-':
			priority = SUB;
			break;
		case '*':
			priority = MUL;
			break;
		case '/':
			priority = DIV;
			break;
		default:
			throw new RuntimeException("未定义运算符:" + operator);
		}
		return priority;
	}
}

/**
 * 使用数组实现栈类
 */
class Stack<E> {
	
	private int maxSize;//栈的最大容量
	private Object[] elements;
	private int top;//指向栈顶元素
	
	public Stack() {
		this(30);
	}
	
	public Stack(int maxSize) {
		this.maxSize = maxSize;
		elements = new Object[this.maxSize];
		top = -1;
	}
	
	/**
	 * 判断栈是否为空的方法
	 * @return 判断是否栈空的结果
	 */
	public boolean isEmpty() {
		return top == -1;
	}
	
	/**
	 * 判断栈是否已满的方法
	 * @return 判断是否栈满的结果
	 */
	public boolean isFull() {
		return top == maxSize - 1;
	}
	
	/**
	 * 元素入栈的方法
	 * @param element 即将入栈的元素
	 */
	public void push(E element) {
		if (isFull()) {
			System.out.println("栈满,元素无法入栈!");
			return ;
		}
		elements[++top] = element;
	}
	
	/**
	 * 取栈顶元素的方法,栈顶元素不出栈
	 * @return 栈顶元素
	 */
	public E peek() {
		if (isEmpty()) {
			throw new RuntimeException("栈空,无法取栈顶元素!");
		}
		return (E)elements[top];
	}
	
	/**
	 * 栈顶元素出栈的方法
	 * @return 栈顶出栈的元素
	 */
	public E pop() {
		if (isEmpty()) {
			throw new RuntimeException("栈空,栈顶元素无法出栈!");
		}
		return (E)elements[top--];
	}
}
运行环境:

jdk: java version “1.8.0_241”
eclipse:
Eclipse IDE for Java Developers
Version: 2019-12 (4.14.0)

  • 3
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
做一门精致,全面详细的 java数据结构与算法!!!让天下没有难学的数据结构,让天下没有难学的算法,不吹不黑,我们的讲师及其敬业,可以看到课程视频,课件,代码的录制撰写,都是在深夜,如此用心,其心可鉴,他不掉头发,谁掉头发???总之你知道的,不知道的,我们都讲,并且持续更新,走过路过,不要错过,不敢说是史上最全的课程,怕违反广告法,总而言之,言而总之,这门课你值得拥有,好吃不贵,对于你知识的渴求,我们管够管饱话不多说,牛不多吹,我们要讲的本门课程内容:稀疏数组、单向队列、环形队列、单向链表、双向链表、环形链表、约瑟夫问题、栈、前缀、中缀、后缀表达式中缀表达式转换为后缀表达式、递归与回溯、迷宫问题、八皇后问题、算法的时间复杂度、冒泡排序、选择排序、插入排序、快速排序、归并排序、希尔排序、基数排序(桶排序)、堆排序、排序速度分析、二分查找、插值查找、斐波那契查找、散列、哈希表、二叉树、二叉树与数组转换、二叉排序树(BST)、AVL树、线索二叉树、赫夫曼树、赫夫曼编码、多路查找树(B树B+树和B*树)、图、图的DFS算法和BFS、程序员常用10大算法、二分查找算法(非递归)、分治算法、动态规划算法、KMP算法、贪心算法、普里姆算法、克鲁斯卡尔算法、迪杰斯特拉算法、弗洛伊德算法马踏棋盘算法。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值