java利用二叉树前、中、后序表达式关系完成一个计算器

额,得说声抱歉,时隔半年回顾的时候,发现之前的确没有做很多的考量,这篇博客内容欠佳。计算器还有有一些功能没有完成,也有许多bug,不过我做了改进,完善了许多bug,详细的,请点击此链接。

1.简单介绍

表达式一般有中缀表达式,后缀表达式和前缀表达式共三种表示形式,其中,中缀表达式是将运算符放在两个操作数之间,例如 1+2 ,此即为平时我们所见到的式子。

后缀表达式也称为逆波兰表达式,是将运算符放在两个操作数之后,而前缀表达式是将运算符放在两个操作数之前。

例如:中缀表达式A+(B-C/D)E,对应的后缀表达式为ABD/-E*+,对应前缀表达式为+A*-B/CDE

计算机对后缀表达式的计算要方便的多(没有运算符优先级与括号约束问题),而中缀表达式又可以通过二叉树的算法转换为后缀表达式(二叉树的前中后序转换相当于前中后缀表达式的转换),基于此原理,我完成了一个简单计算器。

由于我是学数据结构的时候完成此作品,所以主体代码中的队列和栈也是通过数据结构算法实现的,代码也贴在了后面

2.具体实现

import java.awt.BorderLayout;
import java.awt.Color;
import java.awt.Container;
import java.awt.FlowLayout;
import java.awt.GridLayout;
import java.awt.event.ActionEvent;
import java.awt.event.ActionListener;
import java.awt.event.MouseListener;
import javax.swing.*;

public class MyCalculator extends JFrame implements ActionListener,MouseListener{
	private String buttonNames[]= {"7","8","9","+","4","5","6","-","1","2","3","/","0",".","*","="};
	private JButton buttons[] = new JButton[buttonNames.length];//储存按钮的数组
	private JButton clear;
	private JTextField textField;
	private boolean isFirstNumber = true;//作为是不是第一个数字的标志
	private LinkStack stack = new LinkStack();//初始化一个运算符栈
	private LinkStack tempStack = new LinkStack();//储存后缀表达式的结果
	private LinkQueue zhongQueue = new LinkQueue();//初始化一个存中缀的队列
	private LinkQueue houQueue = new LinkQueue();//初始化一个存后缀的队列
	
	//构造方法
	public MyCalculator() {
		// TODO Auto-generated constructor stub
		init();
		this.setBackground(Color.LIGHT_GRAY);
	    this.setTitle("计算器");
	    // 在屏幕(500, 300)坐标处显示计算器
	    this.setLocation(500, 300);
	    // 允许修改计算器的大小
	    this.setResizable(true);
	    // 使计算器中各组件大小合适
	    this.pack();
	    this.setVisible(true);
	    this.setBounds(400, 200, 320, 320);
	    this.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE);
	}
	
	//界面初始方法
	public void init(){
		Container c = getContentPane();
		c.setLayout(new BorderLayout(3,5));//整体布局
		JPanel panel1 = new JPanel(new FlowLayout());
		textField = new JTextField(20);
		textField.setEditable(false);//不允许修改
		textField.setBackground(Color.WHITE);
		panel1.add(textField);
		clear = new JButton("C");
		clear.setBackground(Color.PINK);
		panel1.add(clear);
		clear.addActionListener(this);
		c.add("North",panel1);
		JPanel panel2 = new JPanel(new GridLayout(4,4,4,4));
		for(int i = 0;i<16;i++){
			buttons[i] = new JButton(buttonNames[i]);
			buttons[i].setBackground(Color.white);
			panel2.add(buttons[i]);
			buttons[i].addActionListener(this);
			buttons[i].addMouseListener(this);
		}
		c.add("Center",panel2);	
	}
	
	//事件监听
	@Override
	public void actionPerformed(ActionEvent a) {
		// TODO Auto-generated method stub
		//获取事件源的名字
		String bName = a.getActionCommand();
		if(bName.equals("C")){//当事件源为clear键时
			doClear();
		}else if("1234567890.".indexOf(bName)>=0){//当事件源为数字键时
			doNumber(bName);
		}else if(bName.equals("=")){
			try {
				doAnswer();
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}else{
			try {
				doYunsuan(bName);
			} catch (Exception e) {
				// TODO Auto-generated catch block
				e.printStackTrace();
			}
		}
	}
	
	//响应=的指令
	public void doAnswer() throws Exception{
		zhongQueue.offer(textField.getText());
//		System.out.println(zhongQueue.length());
		int temp = zhongQueue.length();//因为这队列是动态的,所以只能用固定的长度值
		for(int i = 0;(zhongQueue!=null)&&i<temp;i++){
			String c = (String)zhongQueue.poll();//从中缀表达式中出队第一个
			if(!c.equals("")){
				if(isOpenParenthesis(c)){
					stack.push(c);//为开括号,压栈
				}else if(isCloseParenthesis(c)){//为闭括号
					String ac = (String) stack.pop();//弹出栈顶元素
					while(!isOpenParenthesis(ac)){//一直到开括号为止
						houQueue.offer(ac);//添加到后缀表达式的队列
						ac = (String) stack.pop();
					}
				}else if(isOperator(c)){//为运算符
					if(!stack.isEmpty()){//栈非空,取栈顶优先级高的运算符送往后缀表达式
						String ac = (String) stack.pop();
						while(ac!=null&&priority(ac)>=priority(c)){
							houQueue.offer(ac);
							ac = (String) stack.pop();
						}
						if(ac!=null){//若最后一次取出的优先级低的操作符,重新压栈
							stack.push(ac);
						}
					}
					stack.push(c);
				}else{//为操作数,将其添加到后缀表达式的队列末尾
					houQueue.offer(c);
				}
			}
		}
		while(!stack.isEmpty()){//栈中剩余的所有操作符挨个进入后缀表达式
			houQueue.offer(stack.pop());
		}
		//到现在为止,已经将中缀表达式转换成后缀表达式,以链队形式存储
		//对后缀表达式进行求值计算
		
		int temp2 = houQueue.length();
		for(int i = 0;houQueue!=null&&i<temp2;i++){
			String c = (String) houQueue.poll();//从后缀表达式中取出第一个
			if(isOperator(c)){//当为操作符的时候
				double d2 = 0;
				double d1 = 0;
				if(tempStack.length()>1){
					d2 = Double.parseDouble((String)tempStack.pop());
					d1 = Double.parseDouble((String)tempStack.pop());
				}
				//取出两个操作数
//				double d2 = Double.valueOf(tempStack.pop().toString());
//				double d1 = Double.valueOf(tempStack.pop().toString());
				
				double d3 = 0;
				if(c.equals("+")){
					d3 = d2 + d1;
				}else if(c.equals("-")){
					d3 = d2 - d1;
				}else if(c.equals("*")){
					d3 = d2 * d1;
				}else if(c.equals("/")){
					d3 = d1 / d2;
				}
				tempStack.push(d3);
			}else{
				tempStack.push(c);
			}
		}
		textField.setText(""+tempStack.pop());//返回运算结果
	}
	
	//判断字符串是否为运算符
	public boolean isOperator(String c){
		if(c.equals("+")||c.equals("*")||c.equals("/")||c.equals("-")){
			return true;
		}else{
			return false;	
		}
	}
	
	//判断字符是否为开括号,此计算器比价简单,此方法不需要写
	public boolean isOpenParenthesis(String c){
		return "(".equals(c);
	}
	//判断字符是否为闭括号,此计算器比价简单,此方法不需要写
	public boolean isCloseParenthesis(String c){
		return ")".equals(c);
	}

	//判断运算法的优先级
	public int priority(String c){
		if(c.equals("*")||c.equals("/")){
			return 2;
		}else if(c.equals("-")||c.equals("+")){
			return 1;
		}else{
			return 0;
		}
	}
	
	//按到了数字键执行操作
	public void doNumber(String name){
		if(isFirstNumber){//如果是第一个数字
			textField.setText(name);
		}else if(name.equals(".")&&textField.getText().indexOf(".")<0){//当前面没有小数点的时候添加()
			textField.setText(textField.getText()+name);
		}else if(!name.equals(".")){
			textField.setText(textField.getText()+name);
		}
		isFirstNumber = false;//然后就不是第一个数字了
	}
	
	//按到清除键执行操作
	public void doClear(){
		textField.setText("");//文本区域置空
		zhongQueue.clear();//中缀表达式置空
		houQueue.clear();//后缀表达式置空
		tempStack.clear();//后缀栈结果置空
		stack.clear();//中间栈结果置空
		isFirstNumber = true;
	}
	
	//按到运算键执行操作
	public void doYunsuan(String name) throws Exception{
		if(textField.getText()!=""&&"+-*/.=".indexOf(textField.getText())<0){//如果此运算符之前是数字
			 zhongQueue.offer(textField.getText());
			 zhongQueue.offer(name);
//			 System.out.println(zhongQueue.length());
			 textField.setText(name);
			 isFirstNumber = true;
		}
	}
	
	//主方法
	public static void main(String[] args) {
		MyCalculator mc = new MyCalculator();
	}

	//以下是完善部分,可以不看
	@Override
	public void mouseEntered(java.awt.event.MouseEvent e) {
	// TODO Auto-generated method stub
		JButton b = (JButton) e.getSource();
		b.setBackground(Color.cyan);
	}

	@Override
	public void mouseExited(java.awt.event.MouseEvent e) {
		// TODO Auto-generated method stub
		JButton b = (JButton) e.getSource();
		b.setBackground(Color.white);
	}
	@Override
	public void mousePressed(java.awt.event.MouseEvent e) {}
	@Override
	public void mouseReleased(java.awt.event.MouseEvent e) {}
	@Override
	public void mouseClicked(java.awt.event.MouseEvent e) {}
}

数据结构中栈和队列的构建如下:

节点类

public class Node {
	public Object date;//存放节点值
	public Node next;//后继节点的引用
	
	//无参数时的构造函数
	public Node(){
		this(null,null);
	}
	
	//带一个参数时的构造函数
	public Node(Object date){
		this(date,null);
	}
	
	//带两个参数的构造函数
	public Node(Object date,Node next){
		this.date = date;
		this.next = next;
	}
}

链栈:

public class LinkStack {
	private Node top;
	
	//将栈置空
	public void clear() {
		// TODO Auto-generated method stub
		top = null;
	}

	//判断栈是否为空
	public boolean isEmpty() {
		// TODO Auto-generated method stub
		return top==null;
	}

	//求栈的长度
	public int length() {
		// TODO Auto-generated method stub
		Node p = top;
		int length = 0;
		while(p!=null){
			p  =p.next;
			++length;
		}
		return length;
	}

	//取栈顶元素并返回其值
	public Object peek() {
		// TODO Auto-generated method stub
		if(!isEmpty())
			return top.date;
		else
			return null;
	}

	//入栈
	public void push(Object x) throws Exception {
		// TODO Auto-generated method stub
		Node p = new Node(x);
		p.next = top;
		top = p;
	}

	//出栈
	public Object pop() {
		// TODO Auto-generated method stub
		if(isEmpty()){
			return null;
		}else{
			Node p = top;
			top = top.next;
			return p.date;
		}
	}

	//输出栈中所有数据元素(顶到底)
	public void display(){
		Node p = top;
		while(p!=null){
			System.out.print(p.date.toString()+" ");
			p = p.next;//指针后移
		}
	}
}

链队:

public class LinkQueue {
	private Node front;//队首指针
	private Node rear;//队尾指针
	
	//构造函数
	public LinkQueue() {
		// TODO Auto-generated constructor stub
		front = rear = null;
	}

	//队列置空
	public void clear() {
		// TODO Auto-generated method stub
		front = rear = null;
	}

	//判空
	public boolean isEmpty() {
		// TODO Auto-generated method stub
		return front==null;
	}

	//求队列长度
	public int length() {
		// TODO Auto-generated method stub
		Node p = front;
		int length = 0;
		while(p!=null){
			p = p.next;
			++length;
		}
		return length;
	}

	//取队首元素
	public Object peek() {
		// TODO Auto-generated method stub
		if(front!=null)
			return front.date;
		else
			return null;
	}

	//入队
	public void offer(Object x) throws Exception {
		// TODO Auto-generated method stub
		Node p = new Node(x);
		if(front!=null){
			rear.next = p;
			rear = p;
		}else{
			front = rear = p;
		}
	}

	//出队
	public Object poll() {
		// TODO Auto-generated method stub
		if(front!=null){
			Node p = front;
			front = front.next;
			if(p==rear)
				rear=null;
			return p.date;
		}else
			return null;
	}

}

3.运行截图

计算器运行截图

4.总结

当然啦,一个计算器本不用如此大费周章,但是,若你能读懂本篇代码,相信一定能对二叉树的理解又上升一个层次!

发布了34 篇原创文章 · 获赞 65 · 访问量 3743

猜你喜欢

转载自blog.csdn.net/baidu_41860619/article/details/103510104
今日推荐