java利用二叉樹前、中、後序表達式關係完成一個計算器

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.總結

當然啦,一個計算器本不用如此大費周章,但是,若你能讀懂本篇代碼,相信一定能對二叉樹的理解又上升一個層次!

發佈了27 篇原創文章 · 獲贊 49 · 訪問量 2273
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章