Android基础实战–实现简单计算器包含复合运算(下)
声明:
简书内容同步:https://www.jianshu.com/u/90ce902439cc
1.本文章为原创文章,转载注明出处,蟹蟹~
2.初学安卓,水平有限,还有很多不足和应当修正的地方,欢迎评论指点
这是简单计算器上篇链接,本文主要概述自己表达式式求值的两种方法:LinkedList和Stack,思路过程极为相似,只是换了一种存储方式。先简单小结LinkedList和Stack的使用。
LinkedList 是一个继承于AbstractSequentialList的双向链表。它也可以被当作堆栈、队列或双端队列进行操作。
LinkedList的本质是双向链表。
在此列出几个常用方法:
void add(int index,E element)
//在此列表中指定位置插入元素,未指定位置则默认在末尾
void addFirst(Object o)
//将指定元素插入列表首部
void addLast(Object o)
//将指定元素插入列表尾部
Object getFirst()
//返回列表首元素
Object getLast()
//返回列表尾部元素
Object removeFirst()
//移除并返回列表首元素
Object removeLast()
//移除并返回列表尾部元素
更多详细用法请查阅API或搜索相关文章,下面这网址挺详细的,推荐一下:
Java 集合系列05之 LinkedList详细介绍(源码解析)和使用示例
栈的总结就很简易了,方法不多:
void push(Object o) //添加元素
Object pop() //删除并返回最后添加的元素(即栈顶元素)
boolean isEmpty() //判断是否为空
int size() //返回栈中元素个数
Object peek() //返回栈顶元素
###运算处理过程
接着上篇文章内容之后就是利用上篇文章中表达式分离后的数据:存放运算数的list集合numl,以及存放运算符的字符串strsign
两种方法最终效果图如下:
####栈解法
准备工作就是定义两个栈,一个放运算符,一个放运算数。两个下标,对应运算数的list集合numl,以及存放运算符的字符串strsign两者的下标。后面的char和两double类型的变量即参与运算时暂时存放数据的变量。还有一个获取优先级函数(其中我将左右括号和等于均视为操作符,优先级数具体在函数中见)。具体如下:
//获取优先级函数
public int getValue(char a) {
int value=0;
switch(a){
case '=':value =0;break;
case ')':value =1;break;
case '(':value =3;break;
case '+':value =1;break;
case '-':value =1;break;
case '*':value =2;break;
case '/':value =2;break;
}
return value;
}
//利用分离的数据:numl的list集合,以及strsign字符串
Stack<Character> ops = new Stack<Character>();
Stack<Double> vals = new Stack<Double>();
int i = 0;// 操作数集合numl下标
int j = 0;// 操作符strsign字符串下标
char symbol;// 符号
double numone, numtwo;// 符号前后两个数
//事先将‘=’和第一个操作数压到相应栈中
if(ops.isEmpty()) {
ops.push('=');
vals.push(numl.get(i++));
}
总体思路即遍历整个操作符strsign和集合numl,通过两个暂存操作数和操作符的栈,根据优先级提取运算数和运算符进行运算,优先级大于前面的运算符就压栈不进行运算,优先级小于等于运算符栈栈顶元素优先级则计算,将结果继续暂存至栈中,直至遍历到strsign末尾(即等号),其运算结果就是此时运算数栈的栈顶元素。
具体过程大致如下:
while(!(strsign.charAt(j)=='='&&ops.peek()=='=')){
//比较符号优先级,当前符号优先级大于前一个则压栈
if( getValue(strsign.charAt(j))>getValue(ops.peek()) ) {
if(strsign.charAt(j)=='(') {
ops.push(strsign.charAt(j++));
continue;
}
vals.push(numl.get(i++));
ops.push(strsign.charAt(j++));
}
//若小于等于当前运算符优先级
else{
//过滤括号
if(strsign.charAt(j)==')'&&ops.peek()=='(') {
j++;
ops.pop();//)不进栈且(出栈 此种情况是括号内运算完之后仅剩一个数
continue;
}
if(ops.peek()=='(') {
//紧接着左括号的运算符,直接正常压栈
vals.push(numl.get(i++));
ops.push(strsign.charAt(j++));
continue;
}
//其他情况直接出栈参与运算(右括号,和同等优先级的运算符)
//运算过程省略
}
运算过程即从操作数栈弹出两个数,操作符中弹出一个操作符,根据操作符对这两个数进行运算即可。运算时需要注意的是先出栈的是后面那一个操作数,即后进先出。运算完后将结果压到操作数中即可。通过这样的while循环过后最终结果即操作数栈中的栈顶元素了。
###LinkedList解法
看文章开始时的总结就不难发现,Stack能做的事情LinkedList都能做,两者的方法完全可以做个类比来等效操作:
比如:
//移除并返回列表尾部元素
Object removeLast() //LinkedList
Object pop() //Stack
//添加元素至尾部
void push(Object o) //LinkedList
void addLast(Object o) //Stack
//返回列表首元素
Object getFirst() //LinkedList
Object peek() //Stack
因此确实如上篇文章结尾处所言,过程几乎一致,只是换了一种存储类型罢了,其实我一开始就是用的LinkedList来解的,多写一种解法只是顺便复习一下数据结构。