额,得说声抱歉,时隔半年回顾的时候,发现之前的确没有做很多的考量,这篇博客内容欠佳。计算器还有有一些功能没有完成,也有许多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.总结
当然啦,一个计算器本不用如此大费周章,但是,若你能读懂本篇代码,相信一定能对二叉树的理解又上升一个层次!