上周搭建了Android环境,这是我第一次尝试的Android项目。
*前期知识准备*
*Java集合-------> 栈与队列------->中缀表达式转后缀表达式(便于计算机处理表达式)&&计算后缀表达式
这里附上一个一个栈实现的括号配对。
【栈经常用于解析某种类型的文本串,这是一个检测用户输入的一行文本串中分隔符的程序,分隔符有大括号,中括号,小括号,每个左分隔符需要和右分隔符匹配,(也就是输入过程一种左分隔符的数量一定大于等于同类型右分隔符的数量),在字符串中后出现的分隔符要比先出现的分隔符更早完成匹配。/*分隔匹配符程序从栈中不断地读取字符,每读取一个字符如果发现它是一个左分隔符就将其压入栈中。当从输入中读取到一个右分隔符时,弹出栈顶的左分隔符,并且查看它是否与右分隔符匹配,如果不匹配,比如左{右),程序就要报错。非分隔符字符不插入栈,略过即可*/】
package abb;
import java.io.*;
public class stackd {
private int maxSize;
private char[] stackArray;
private int top;
public stackd(int s){
maxSize=s;
stackArray=new char[maxSize];
top=-1;
}
public void push(char j)
{
stackArray[++top]=j;
}
public char pop()
{
return stackArray[top--];
}
public char peek()
{
return stackArray[top];
}
public boolean isEmpty()
{
return(top==-1);
}
}
class BracketChecker(String in)
{{input=in;}
public void check()
{
int stackSize=input.length();
Stack theStack=new StackX(stackSize);
stackd thestack=new stackd(stackaSize);
}
for(int j=0;j
*逆波兰表达式------>计算时分两步走:先将中缀表达式 转为后缀表达式,再对后缀表达式进行计算*
[逆波兰表达式使用栈进行数字的存储。将中缀表达式转为后缀表达式:1.从左至右扫描一遍中缀表达式2.如果读取的是操作数,则判断该操作数的类型,并将该操作数存入操作数堆栈,若读取的是运算符,①运算符为左括号直接存入运算符堆栈②运算符为右括号,输出运算符堆栈中的操作符到操作数堆栈,直到遇到左括号为止③运算符为非括号运算符:若操作符堆栈栈顶元素为括号,则直接进入操作符堆栈;若比栈顶操作符的优先级高或者相等,直接进入操作符堆栈;若比运算符堆栈栈顶的运算符优先级低,则输出栈顶运算符到操作数堆栈,并将当前运算符压入运算符堆栈。4.当表达式读取完成后运算符堆栈中尚有运算符时,则依序取出运算符到操作数堆栈,直到运算符堆栈为空。]
这里同样的,附上一道逆波兰表达式的题目。
人们的日常习惯是把算术表达式写成中缀式,但对于机器来说更“习惯于”后缀式,关于算术表达式的中缀式和后缀式的论述一般的数据结构书都有相关内容可供参看,这里不再赘述,现在你的任务是将中缀式变为后缀式。
输入
第一行输入一个整数n,共有n组测试数据(n<10)。
每组测试数据只有一行,是一个长度不超过1000的字符串,表示这个运算式的中缀式,每个运算式都是以“=”结束。这个表达式里只包含+-*/与小括号这几种符号。其中小括号可以嵌套使用。数据保证输入的操作数中不会出现负数。
数据保证除数不会为0
输出
每组都输出该组中缀式相应的后缀式,要求相邻的操作数操作符用空格隔开。
先不附代码了,哪天闲了,高兴了再慢慢做。
布局:我用的是线性布局。因为我就学到了线性布局,想用别的也没什么不可以的。
###第一版的计算器:
功能:实现了带括号的四则运算
问题:没有异常处理,
界面没有吸引力
未能实现负数的运算
UI部分代码:
package com.example.lenovo.calculator;
import android.support.v7.app.AppCompatActivity;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;
import android.widget.EditText;
import android.widget.TextView;
import static com.example.lenovo.calculator.IndexInToDuffix.Houzhui;
public class MainActivity extends AppCompatActivity implements View.OnClickListener
{
private Button btn_0;
private Button btn_1;
private Button btn_2;
private Button btn_3;
private Button btn_4;
private Button btn_5;
private Button btn_6;
private Button btn_7;
private Button btn_8;
private Button btn_9;
private Button btn_point;
private Button btn_plus;
private Button btn_sub;
private Button btn_multi;
private Button btn_div;
private Button btn_left;
private Button btn_right;
private Button btn_ac; //清空
private Button btn_del;//删除
private Button btn_equals; //等号
private TextView et_showview;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
initView();
}
public void initView() {
btn_0 = (Button) findViewById(R.id.btn_0);
btn_1 = (Button) findViewById(R.id.btn_1);
btn_2 = (Button) findViewById(R.id.btn_2);
btn_3 = (Button) findViewById(R.id.btn_3);
btn_4 = (Button) findViewById(R.id.btn_4);
btn_5 = (Button) findViewById(R.id.btn_5);
btn_6 = (Button) findViewById(R.id.btn_6);
btn_7 = (Button) findViewById(R.id.btn_7);
btn_8 = (Button) findViewById(R.id.btn_8);
btn_9 = (Button) findViewById(R.id.btn_9);
btn_point = (Button) findViewById(R.id.btn_point);
btn_div = (Button) findViewById(R.id.btn_div);
btn_plus = (Button) findViewById(R.id.btn_plus);
btn_sub = (Button) findViewById(R.id.btn_sub);
btn_multi = (Button) findViewById(R.id.btn_multi);
btn_left = (Button) findViewById(R.id.btn_left);
btn_right = (Button) findViewById(R.id.btn_right);
btn_equals = (Button) findViewById(R.id.btn_equals);
btn_ac = (Button) findViewById(R.id.btn_ac);
btn_del = (Button) findViewById(R.id.btn_del);
btn_0.setOnClickListener(this);
btn_1.setOnClickListener(this);
btn_2.setOnClickListener(this);
btn_3.setOnClickListener(this);
btn_4.setOnClickListener(this);
btn_5.setOnClickListener(this);
btn_6.setOnClickListener(this);
btn_7.setOnClickListener(this);
btn_8.setOnClickListener(this);
btn_9.setOnClickListener(this);
btn_equals.setOnClickListener(this);
btn_del.setOnClickListener(this);
btn_right.setOnClickListener(this);
btn_left.setOnClickListener(this);
btn_multi.setOnClickListener(this);
btn_sub.setOnClickListener(this);
btn_plus.setOnClickListener(this);
btn_div.setOnClickListener(this);
btn_ac.setOnClickListener(this);
btn_point.setOnClickListener(this);
et_showview = (TextView) findViewById(R.id.msg);
}
@Override
public void onClick(View view) {
String str=et_showview.getText().toString();
switch (view.getId()) {
case R.id.btn_0:
str+="0";
et_showview.setText(str);
break;
case R.id.btn_1:
str+="1";
et_showview.setText(str);
break;
case R.id.btn_2:
str+="2";
et_showview.setText(str);
break;
case R.id.btn_3:
str+="3";
et_showview.setText(str);
break;
case R.id.btn_4:
str+="4";
et_showview.setText(str);
break;
case R.id.btn_5:
str+="5";
et_showview.setText(str);
break;
case R.id.btn_6:
str+="6";
et_showview.setText(str);
break;
case R.id.btn_7:
str+="7";
et_showview.setText(str);
break;
case R.id.btn_8:
str+="8";
et_showview.setText(str);
break;
case R.id.btn_9:
str+="9";
et_showview.setText(str);
break;
case R.id.btn_div:
if(str.length()!=0){
str+="/";
et_showview.setText(str);
}break;
case R.id.btn_multi:
if(str.length()!=0){
str+="*";
et_showview.setText(str);
}break;
case R.id.btn_point:
if(str.length()!=0 && (str.charAt(str.length())>='0'&&str.charAt(str.length())<=9)){
str+=".";
et_showview.setText(str);
}break;
case R.id.btn_plus:
if(str.length()!=0){
str+="+";
et_showview.setText(str);
}break;
case R.id.btn_sub:
if(str.length()!=0){
str+="-";
et_showview.setText(str);
}break;
//如果运算符和小数点不出现在首尾位置,可以添加进字符串里
//小数点前一位必须是数字
case R.id.btn_left:
str+="(";
et_showview.setText(str);
break;
case R.id.btn_right:
str+=")";
et_showview.setText(str);
break;
case R.id.btn_equals:
et_showview.setText(IndexInToDuffix.calc(Houzhui(str))); //输入结束,转为逆波兰表达式
break;
case R.id.btn_del:
str=str.substring(0,str.length()-1);
et_showview.setText(str);
//如果字符串长度大于等于1,就前删一个字符
break;
case R.id.btn_ac:
str="";
et_showview.setText(str);
break;
}
}
}
核心算法代码
package com.example.lenovo.calculator;
import java.util.HashMap;
import java.util.Map;
import java.util.Scanner;
import java.util.Stack;
import java.util.ArrayList;
import java.math.BigDecimal;
public class IndexInToDuffix {
public static Stack Houzhui(String s)
{
Stack stacka=new Stack(); //存放操作数
Stack stackb=new Stack(); //存放操作符
//需要创建一个字符串存放数,不然2位数及以上会被拆开,不方便后续处理
String temp=new String();
HashMap hashMap = new HashMap();
hashMap.put("(", 0);// 设定优先级 +-优先级相同 */优先级相同
hashMap.put("+", 1);
hashMap.put("-", 1);
hashMap.put("*", 2);
hashMap.put("/", 2);
for(int i=0;i1){
String t=stackb.pop();
stacka.push(t);
stackb.push(m);
}else{
stackb.push(m);
}
break;
case '*':
case '/':
stackb.push(m);
break;
}
}
}
}
while(!stackb.isEmpty()){
String q=stackb.pop();
stacka.push(q);
}
return stacka;
}
public static String calc(Stack stacka)//计算逆波兰表达式
{
ArrayList arr=new ArrayList();
while(!stacka.isEmpty()){
String t=stacka.pop();
//System.out.println("t="+t);
arr.add(t);
}
ArrayList arr1=new ArrayList();
for(int i=arr.size()-1;i>=0;i--){
int j=arr1.size();
switch(arr.get(i)){
case "+":
BigDecimal a=new BigDecimal(arr1.remove(j-2)).add(new BigDecimal(arr1.remove(j-2)));
arr1.add(String.valueOf(a));
break;
case "-":
BigDecimal b=new BigDecimal(arr1.remove(j-2)).subtract(new BigDecimal(arr1.remove(j-2)));
arr1.add(String.valueOf(b));
break;
case "*":
BigDecimal c=new BigDecimal(arr1.remove(j-2)).multiply(new BigDecimal(arr1.remove(j-2)));
arr1.add(String.valueOf(c));
break;
case "/":
BigDecimal d=new BigDecimal(arr1.remove(j-2)).divide(new BigDecimal(arr1.remove(j-2)));
arr1.add(String.valueOf(d));
break;
default: arr1.add(arr.get(i)); break;
}
}
return arr1.get(0);
}
}
###第二版计算器
像除0,运算符连在一起之类的异常情况被监听器检测到后不能输入进去,
如果运算结束后括号数目不匹配就会显示“input error”信息
可以对负数进行运算了但前提是必须用括号括起来,如果第一个数是负数,没有括号将无法输入(这个设计有点反人类了,不提倡)
第一版发现不能整除时会有问题,后来发现要在运算那里设置一个四舍五入保留精度,问题遂解决
界面按钮颜色调整
为简洁起见,我只附上一些紧要处的修改代码。更多细节请读者自行处理调整。
case R.id.btn_0:
if (str.isEmpty() || str.charAt(str.length() - 1) != '/') {
str += "0";
et_showview.setText(str);
}
break;
case R.id.btn_div:
if (str.length() != 0) {
if (str.charAt(str.length() - 1) != '+') {
if (str.charAt(str.length() - 1) != '-') {
if (str.charAt(str.length() - 1) != '*') {
if (str.charAt(str.length() - 1) != '/') {
str += "/";
et_showview.setText(str);
}
}
}
}
}
break;
case R.id.btn_equals:
if(str.length()==0)
break;
StringBuilder st = new StringBuilder(str);
for (int i = 1; i < st.length(); i++) {
if (st.charAt(i) == '-' && (st.charAt(i - 1) == '(')) {
st.insert(i, "0");
}
}
int m = 0;
int n = 0;
for (int j = 0; j < str.length(); j++) {
if (str.charAt(j) == '(')
m++;
else if (str.charAt(j) == ')')
n++;
}
if (iferror == true)
break;
if (m != n) {
et_showview.setText("input error");
iferror = true;
break;
}
try {
et_showview.setText(IndexInToDuffix.calc(Houzhui(st))); //输入结束,转为逆波兰表达式
} catch (Exception e) {
et_showview.setText("calculate failed");
}
break;
switch(arr.get(i)){
case "":
break;
这个版本运用了异常处理,极大地减少了运行停止的情况。计算器通常都能稳定的正常运行,也能保证每次保存上次的结果并在此基础上继续运算,但出现过莫名其妙的卡死状况,重启APP后恢复正常。
第一次做APP,一开始心里也没底,硬着头皮写了2天也就写出来了,回头看觉得其实真的没多少东西。逆波兰表达式那里认真看看就突破这个难关了。还是挺开心的。一点一点的远离废柴这个身份。过两天如果还有后续优化,我会附在下一篇博客里的。