栈的运用--普通计算器和逆波兰计算器

用栈实现计算器,这个比较难。有兴趣和热情的朋友,你可以看看。没有的,建议你看下一个数据结构。

逆波兰计算器是我们的终极目标,在实现目标之前,需要不断积累,所以我们一步步来:

普通单位加减乘除计算器-->普通多位加减乘除计算器-->前中后缀表达式规则-->逆波兰计算器

单位加减乘除实现:输入一个字符串“2+2*5-2”,通过栈计算出答案

算法原理:

栈结构,我选取上面实现的链表模拟栈;在原有基础上,新增 isEmpty,getTop(获取栈顶),priority(获取操作符的优先级),isOper(判断是否为运算符),cal(输入两个数字与一个运算符进行数学运算)方法,具体实现见代码;

创建numStick用于存储数字,创建operStick用于存储运算符,对字符串进行逐项扫描,每扫描一个字符判断其是否为运算符(+-*/)

是运算符时,就判断operStick空吗(isEmpty实现)?空就直接入栈,不空就要进行当前运算符与栈顶运算符(用getTop获取)进行优先级的比较(priority实现)

当前<=top,则将numStick中的两个栈点进行出栈,将operStick的栈顶进行出栈,这些出栈数据统统装入cal方法进行运算,运算结果放入numStick中,当前运算符入栈。当前>top,当前运算符直接入栈

不是运算符时,就直接入numStick栈;在上述判断执行完后,继续循环执行,直到字符串中所有字符都被扫描即可;

扫描完成后,所有数据被列入栈中,上述操作的目的旨在将高级运算在入栈时,运算干净,这样栈里都是同级的运算

下面对栈中的同级运算数据进行运算,运算过程就是numStick出两个,operStick出一个,进入cal方法中运算,结果继续入numStick栈,然后接着重复计算,直到operStack中没有运算符残留,就结束。

那么numStack中一定只剩下一个数字,而它就是我们苦苦寻找的结果!

package cn.dataStructure.demo.stack.calculator;class DoubleNode{public int data;public DoubleNode next;//保存下一个结点public DoubleNode pre;//保存上一个结点public DoubleNode(int data){this.data =data;}@Overridepublic String toString() {return "["+this.data +"]";}
}
class DoubleLinkedListStack{private DoubleNode root=new DoubleNode(0);//创建有头根节点public DoubleNode getRoot(){return this.root;}public void push(int data){//普通add:不按序号存储DoubleNode temp=this.root;//root不能动,需要辅助结点while (true){//查找链表最后结点if (temp.next==null){break;}temp=temp.next;//向后一位}DoubleNode newNode=new DoubleNode(data);temp.next=newNode;//尾结点双向添加newNode.pre=temp;}public int pop(){if (root.next==null){//判断是否为空链表throw new RuntimeException("空栈");}DoubleNode temp=root;while (temp.next!=null){//遍历到链表最后temp=temp.next;}int value=temp.data;temp.pre.next=null;//自我删除return value;}public void show(){//逆向打印if (root.next==null){//判断是否为空链表System.out.println("空栈");return;}DoubleNode temp=root;while (temp.next!=null){//遍历到链表最后temp=temp.next;}while (temp!=root){//逆向打印System.out.println(temp);temp=temp.pre;}}//判断栈空public boolean isEmpty(){return root.next==null;}//返回栈顶的值public int getTop(){if (root.next==null){//判断是否为空链表throw new RuntimeException("空栈");}DoubleNode temp=root;while (temp.next!=null){//遍历到链表最后temp=temp.next;}return temp.data;}//返回运算符优先级,正比public int prioprity(int oper){if (oper=='*'||oper=='/'){return 1;}else if (oper=='+'||oper=='-'){return 0;}else {return -1;//暂且支持加减乘除}}//判断是否为加减乘除public boolean isOper(int oper){return oper=='+'||oper=='-'||oper=='/'||oper=='*';}//对数据进行计算public int cal(int num1,int num2,int oper){int result=0;//存储结果switch (oper){case '+':result=num1+num2;break;case '-'://栈先入后出,所以2-1result=num2-num1;break;case '*':result=num1*num2;break;case '/'://栈先入后出,所以2-1result=num2/num1;break;}return result;}
}
public class 栈实现计算器 {public static void main(String[] args) {DoubleLinkedListStack numStack=new DoubleLinkedListStack();//数字栈DoubleLinkedListStack operStack=new DoubleLinkedListStack();//运算符栈String str="2+5*2-2";//10int index=0;//对str进行扫描的索引char ch=' ';//保存每次扫描到的字符int num1;//存储要计算的数字int num2;int value;//用于承接临时计算数据//对字符串进行扫描,将所有数据录入栈中while (true){ch=str.substring(index,index+1).charAt(0);if (operStack.isOper(ch)){//判断是否为操作符if (operStack.isEmpty()){//操作符栈空时,扫描到的操作符直接入栈operStack.push(ch);}else if (operStack.prioprity(ch)<=operStack.prioprity(operStack.getTop())){//判断当前操作符的优先级<=栈顶操作符的优先级num1=numStack.pop();num2=numStack.pop();value=operStack.cal(num1,num2,operStack.pop());numStack.push(value);operStack.push(ch);}else{//当前操作符的优先级>栈顶操作符的优先级operStack.push(ch);}}else {numStack.push(ch-48);//数字是按char接收的,所以要减去48,从char恢复到int}index++;if (index==str.length()){//此时字符串中所有数据被扫描完break;}}//对栈中数据进行计算while (true){if (operStack.isEmpty()){//当操作符栈空,数据计算完成break;}num1=numStack.pop();num2=numStack.pop();value=operStack.cal(num1,num2,operStack.pop());numStack.push(value);}System.out.printf("%s=%d",str,numStack.pop());}
}
2+5*2-2=10

开心了吗?其实还是有点不开心,因为上面这个只能是一位数字运算,多位数程序就崩了,原因在于我们在数字入栈前没有判断下一个字符是不是数字。那么我们开始着手升级

多位加减乘除实现:输入一个字符串“2+2*5-20”,通过栈计算出答案

算法分析:

修改字符串

String str="2+5*2-20";//-8

新增number用于处理多位计算,将多次读入的字符合并

String number="";//用于处理多位计算,将多次读入的字符合并

对数字入栈进行升级

//对字符串进行扫描,将所有数据录入栈中while (true){ch=str.substring(index,index+1).charAt(0);if (operStack.isOper(ch)){//判断是否为操作符if (operStack.isEmpty()){//操作符栈空时,扫描到的操作符直接入栈operStack.push(ch);}else if (operStack.prioprity(ch)<=operStack.prioprity(operStack.getTop())){//判断当前操作符的优先级<=栈顶操作符的优先级num1=numStack.pop();num2=numStack.pop();value=operStack.cal(num1,num2,operStack.pop());numStack.push(value);operStack.push(ch);}else{//当前操作符的优先级>栈顶操作符的优先级operStack.push(ch);}}else {//对下面这句话进行修改,实现多位运算支持//numStack.push(ch-48);//数字是按char接收的,所以要减去48,从char恢复到intnumber+=ch;//合并所有扫描到的数字if (index==str.length()-1){//若该数字是最后一位,无需判断直接入栈numStack.push(Integer.parseInt(number));}else {//判断下一个字符是数字吗?不是则入栈if (operStack.isOper(str.substring(index+1,index+2).charAt(0))){numStack.push(Integer.parseInt(number));number="";//清空number}}}

效果如下

2+5*2-20=-8

上面的计算器实现最麻烦的是,要将人看的懂的表达式转化为机器能看懂的表达式!解决这个问题的办法----波兰表达式。

前中后缀表达式规则

前缀即波兰表达式,中缀即人看的表达式,后缀即逆波兰表达式,上面我们实现的计算机属于中缀计算器。其中逆波兰表达式,计算机最懂,所以我以逆波兰为主,制作一个逆波兰计算器

逆波兰计算器实现

算法分析:

为了简化代码复杂度,使用系统提供的类集框架代替自己写的链表与栈

将逆波兰表达式分割为一个个单独的字符串数组,通过逆波兰表达式阅读规则:遇到运算符就出栈两个数字,编写计算方法

package cn.dataStructure.demo.stack.calculator;
import java.util.ArrayList;
import java.util.List;
import java.util.Stack;public class 逆波兰计算器 {public static void main(String[] args) {String str="2 5 * 8 2 / + 1 -";//(2*5)+8/2-1-->2 5 * 8 2 / + 1 -;13List<String> list=getListString(str);System.out.printf("(2*5)+8/2-1=%d",calculate(list));}//分割逆波兰表达式入list存储public static List<String> getListString(String str){String split[]=str.split(" ");List <String> list=new ArrayList<>();for (String temp:split) {list.add(temp);}return list;}//逆波兰表达式计算public static int calculate(List<String> list){Stack<String> stack=new Stack<>();for (String temp:list){if (temp.matches("\\d+")){//正则匹配多位数字stack.push(temp);}else {int num1= Integer.parseInt(stack.pop());int num2= Integer.parseInt(stack.pop());int value=0;switch (temp){case "+":value=num2+num1;break;case "-":value=num2-num1;break;case "*":value=num2*num1;break;case "/":value=num2/num1;break;default:throw new RuntimeException("运算符有误");}stack.push(""+value);//利用+将int转为string}}return Integer.parseInt(stack.pop());}
}
2*5+8/2-1=13

你有没有一个疑问,有没有可以把中缀表达式自动转化为逆波兰表达式的算法?有,如下:

中缀-->逆波兰

举个例子:“1+((2+3)*4)-5”-->“1 2 3 + 4 * + 5 -”

在整个过程中,s1不断进行入栈,出栈操作,所以s1一定为栈结构,但s2只有入栈,没有出栈,且若s2以栈结构时,最后输出逆波兰表达式时,需要逆向输出。所以我们使用ArrayList来代替栈结构。

在上面的代码中我们已经实现了逆波兰表达式计算,下面实现中缀表达式转逆波兰。算法流程和上面图片中描述的一样

算法分析:

1,对getListString方法进行改造,使之可以对中缀表达式字符串进行分割,该算法支持多位整数。

public static List<String> getListString(String str){List<String> list=new ArrayList();int index=0;//字符串索引标记char ch;//拆分的字符String string;//拼接的字符串do {ch=str.charAt(index);if (ch<48||ch>57){//非数字list.add(""+ch);index++;}else {//是数字,考虑多位问题string="";//index不能越界,且下一位仍然为数字while (index<str.length()&&(ch=str.charAt(index))>=48&&(ch=str.charAt(index))<=57){string+=ch;index++;}list.add(string);}}while (index<str.length());return list;}

2,新增parseSuffix方法,用于将拆分好的中缀表达式转化为逆波兰表达式。

这里有个坑:进行字符串比较的时候要用equals方法,不要用==比较。这里牵扯到Java字符串底层机制(入池与不入池),详见我的Java SE博客:【String类-字符串比较】

//中缀表达式转逆波兰表达式的方法public static List<String> parseSuffix(List<String> list){Stack<String> s1=new Stack<>();//s1用于存储运算符List<String > s2=new ArrayList();//s2用于存储结果for (String temp:list){if (temp.matches("\\d+")){//是数字,入s2栈s2.add(temp);}else {//非数字(运算符,括号)//是括号if (temp.equals("(")){//(,直接入栈s1.push(temp);}else if (temp.equals(")")){//),不断s1弹出运算符入s2,直到遇到s1的(为至,并将这一对括号丢弃while (!s1.peek().equals("(")){s2.add(s1.pop());}s1.pop();//抛弃括号}else if (s1.isEmpty()){//s1空,直接入栈s1.push(temp);}else if (s1.peek().equals("(")){//s1的栈顶为(,直接入栈s1.push(temp);}else if (getPriority(temp)>getPriority(s1.peek())){//当前运算符优先级>栈顶优先级,直接入栈s1.push(temp);}else {while (s1.size()!=0&&getPriority(temp)<=getPriority(s1.peek())){s2.add(s1.pop());}s1.push(temp);}}}//将s1剩余字符串依次加入s2while (s1.size()!=0){s2.add(s1.pop());}return s2;}

3,parseSuffix方法需要getPriority方法(获取操作符优先级)支持

//获取优先级public static int getPriority(String oper){int value=0;switch (oper){case "+":value=1;break;case "-":value=1;break;case "*":value=2;break;case "/":value=2;break;}return value;}

4,在主方法编辑测试代码

String str="1+((2+3)*4)-5";//1 2 3 + 4 * + 5 -List<String> list=getListString(str);//拆分字符串List<String> suffix=parseSuffix(list);//获取逆波兰表达式System.out.println(str+"="+calculate(suffix));
1+((2+3)*4)-5=16

【数据结构与算法整理总结目录 :>】<-- 宝藏在此(doge)

栈结构应用_普通计算器和逆波兰计算器相关推荐

  1. c语言逆波兰计算器程序,逆波兰计算器(C语言)

    源自<The C Programming Language> P62 ex4.3: 计算例如:(1 - 2) * (4 + 5)的值,采用逆波兰表示法(即后缀表示法) 代码: main.c ...

  2. 数据结构:栈实现逆波兰计算器

    栈实现逆波兰计算器 前言 上篇博文中已经介绍了栈实现中缀表达式计算器,中缀表达式形如 "1+((2+3)*4)-5" 对人比较容易计算,但对于计算机却是一件比较困难的事,而后缀表达 ...

  3. 数组结构与算法-036-042 前中后缀表达式-逆波兰计算器

    036 前缀 中缀 后缀(逆波兰表达式)表达式 前缀表达式 前缀表达式(波兰表达式) 前缀表达式又称波兰表达式,前缀表达式的运算符位于操作数之前 举例说明:(3 + 4) * 5 -6 对应的前缀表达 ...

  4. 逆波兰计算器android源码简书,计算器的核心算法-JavaScript实现(逆波兰表达式)...

    最终计算器的掩饰效果,欢迎大家来找BUG. http://codepen.io/lvanboy/full/LxKVxJ/ 功能: 1.按照运算符的优先级运算 2.利用上次的结果继续运算 3.多个数字混 ...

  5. c语言逆波兰计算器程序,c语言实现逆波兰计算器

    最近学算法学到了栈 写了一个典型例子逆波兰计算器.用很像链表的形式写的下面来看思路. 首先要做的是一个栈的模型具有压栈和出栈的功能 #include #include #define MAX_S 20 ...

  6. 八、逆波兰计算器的分析与实现

    逆波兰计算器的分析与实现 一.案例 1.输入一个逆波兰表达式(后缀表达式),使用栈(Stack), 计算其结果 2.例如: (3+4)×5-6 对应的后缀表达式就是 3 4 + 5 × 6 - , 针 ...

  7. 逆波兰计算器(含完整版)

    逆波兰计算器 package com.hsy.stack;import java.util.ArrayList; import java.util.List; import java.util.Sta ...

  8. 数据结构 - 栈 (逆波兰计算器)(栈的三种表达式)(前缀、中缀和后缀表达式,后缀也叫逆波兰表达式)(中缀表达式转后缀表达式实现步骤及完整代码)

    栈的三种表达式:前缀.中缀和后缀表达式,后缀也叫逆波兰表达式 前缀(波兰表达式) 中缀(对人来讲很好理解,对于计算机来讲就方便了,一般会把中缀表达式转换成后缀表达式) 后缀(逆波兰表达式) 计算过程 ...

  9. 使用栈实现中缀表达式转换成后缀表达式并计算结果(逆波兰计算器)

    一.中缀表达式转换成后缀表达式 具体步骤如下: 1.初始化栈stack(暂时存放运算符)以及集合list(存放后缀表达式) 2.从左向右扫描中缀表达式 3.当前元素为数字时,直接添加到list中 4. ...

最新文章

  1. 【廖雪峰python入门笔记】list添加元素_append()和insert()
  2. 哪种编程语言又快又省电?有人对比了27种语言
  3. 当 RocketMQ 遇上 Serverless,会碰撞出怎样的火花?
  4. JS和C#访问遇到QueryInterface调用出错
  5. LINQ中判断日期时间段
  6. c语言怎么在服务器端查询进程列表,C语言 在服务器端识别客户端的方法
  7. tomcat(2)一个简单的servlet容器
  8. 【Git、GitHub、GitLab】一 Git安装与Git最小配置
  9. 将阿拉伯数字转换成中文大写的好算法
  10. SAP Control framework–实例
  11. db2 空值转换函数_Excel一键转换百分比
  12. 二叉线索树的线索化以及遍历
  13. 校园无盘服务器,校园微机系统优化及无盘改造实例.docx
  14. slf4j 和 log4j2 架构设计
  15. jquey知识点整理
  16. xvid-core1.1.2编译方法(vc6,vs2005)
  17. 竹云+巨杉丨互信认证 安全可靠
  18. yii学习笔记—gii 自动代码生成工具
  19. 鸿蒙纪元1.1正式版隐藏,毁灭纪元1.1.1官方版附隐藏英雄密码
  20. CPU 缓存一致性 MESI 协议

热门文章

  1. 2022年湖南医院三基考试每日一练及答案
  2. 制作光盘安装linux系统教程,在Windows上制作CentOS自动安装的光盘的教程
  3. 23年2月CCF会议截稿8条-SACMAT2023/UAI2023/Euro-Par2023/ASAP2023/ICCCN2023/MobHoc2023/ICCBR2023/PETS2023
  4. php 生产一维码,php生成条码函数,PHP生成一维码函数
  5. 融资租赁行业在新监管下,强化信息化建设,助推业务发展,规范经营行为
  6. id nfc模拟_NFC卡模拟app-NFC卡模拟安卓版下载v6.0.0 - 7230手游网
  7. 用户和计算机通信的界面设计,数据分析系统的交互界面设计
  8. 摄影后期用计算机配置,摄影修图需要高配置电脑吗?最好搞懂几个问题……
  9. sh脚本和bash脚本_使用此简单的Bash脚本在家打印双面文档
  10. 【云栖大会】飞天进化:从操作系统到人工智能