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