K:逆波兰算法
相关介绍:
一种求解字符串形式的表达式的结果的算法,该算法在求解时,需要先将我们平日里习惯上使用的中序表达式的模式转化为等价的后序(后缀)表达式的模式,之后再通过求解出该后序(后缀)表达式的结果而得到原中序表达式的结果,为此,该算法主要有两个任务。第一是将中序表达式转化为后序(后缀)表达式的形式。第二是通过求解后序(后缀)表达式的结果,从而得到原中序表达式的结果。
所谓的中序表达式,就是将运算符放在两个操作数的中间,就是我们平日里所使用的算数表达式的形式,如:1+2+(34+1)*23,10-2等,而后序表达式就是将运算符放在两个操作数之后,如中序表达式A+(B-C/D)*E对应的后序(后缀表达式)为ABCD/-E*+。由于运算符有优先级,为此在计算机内部使用中序表达式来描述时,对计算是非常不方便的,特别是带括号时更加麻烦。而后序表达式中既无运算符优先级,又无括号的约束问题因此在后序表达式中运算符出现的顺序正是计算的顺序,所以计算一个后序表达式比计算一个中序表达式的值简单得多。
中序表达式转化为后序表达式:
由于中序表达式与后序表达式中的操作数所出现的先后次序是完全一样的,只是运算符出现的先后次序不同,为此,将中序表达式转化为后序表达式,其重点是放在运算符的处理上。首先设定运算符的优先级如下表所示:
运算符 | +(加)、-(减) | *(乘)、/(除) |
---|---|---|
优先级 | 1 | 2 |
其中,其数值越大的表示其运算符的优先级越高。
要使其运算符出现的次序与真正的算术运算符顺序一致,就要使优先级高的以及括号内的运算符先出现在前面,所以在把算术表达式转化为后序表达式的时候,要使用一个栈来保留还未送往后缀表达式的运算符,此栈称为运算符栈。其算法的基本思想如下:
- 初始化一个运算符栈
- 从算术表达式输入的字符串中从左到右读取一个字符
- 若当前字符是操作数,则直接送往后缀表达式
- 若当前字符是左括号“(”,则将其压入运算符栈
- 若当前字符为运算符时,则
- 当运算符栈为空时,将该运算符直接压入运算符栈中
- 当此运算符的优先级高于栈顶元素时,将此运算符压入运算符栈;否则,弹出栈顶运算符送往后序表达式,并将当前运算符压栈,重复步骤5
- 若当前字符是右括号")"时,反复将栈顶符号弹出,并送往后序表达式,直到栈顶运算符为左括号"("为止,再将左括号出栈并丢弃
- 若读取未完成,则跳转到步骤2
- 若读取完毕,则将栈中剩余的所有运算符弹出栈,并送往后序表达式中
其实现代码如下:
public class ReverseBoLan
{
//该符号表用于定义运算符的优先级
private Map<String,Integer> table=getMap();
//该字符串为用于匹配数字的正则表达式
private String rexNumber="\\d+";
private String rexOperator="[((+\\-*/×÷))]";
//该字符串为用户输入的中序表达式
private String input;
//该栈对象用于存储运算符和括号(左括号)
private Stack<String> save=new Stack<String>();
/**
* 该方法用于将中序表达式转化为后序表达式,并对其转化后的表达式以字符串的形式进行返回
* @return 后序表达式
*/
public String reverse()
{
//result变量用于存储结果
String result="";
//将输入的中序表达式中的数字存储到number数组中
String[] number=input.split(rexOperator);
int order=0;
int i=0;
while(i<input.length())
{
//获得当前字符
String thisString=String.valueOf(input.charAt(i));
//当前该字符为运算符或者括号时,即当前该字符不为数字时
if(thisString.matches(rexOperator))
{
//当当前字符不为左括号或者右括号时(即为运算符)
if(!thisString.matches("[()()]"))
{
//用于记录栈顶元素的优先级
int temporary=0;
//获取当前字符的优先级
int present=table.get(thisString);
//当操作数的栈不为空的时候
if(!save.isEmpty())
{
//查看栈顶元素的字符以及其优先级
String top=save.getTop();
if(!top.matches("[((]"))
{
temporary=table.get(top);
}
}
//当栈顶元素的操作符的优先级比当前操作符的优先级还要高或者相同时,对其进行弹出操作,直到栈顶元素的优先级比当前操作符的优先级要低
if(temporary>=present)
{
while(!save.isEmpty()&&table.get(save.getTop())>=present)
{
result+=" "+save.pop();
}
}
save.push(thisString);
}
//当当前的字符为左括号的时候,直接将其压入栈中
else if(thisString.matches("[((]"))
{
save.push(thisString);
}
//当当前的字符为右括号的时候,将其栈中的元素一直弹出,直至遇到左括号结束,并将左括号弹出
else
{
while(!save.getTop().matches("[((]"))
result+=" "+save.pop();
save.pop();
}
i++;
}
//当前该字符为数字的时候
if(thisString.matches(rexNumber))
{
//用于存储数字数组中的数字字符串
String numberString=null;
do
{
numberString=number[order];
//当数字字符串中的数字不为空时(由于可能会是空字符串的出现),将整个中序表达式的字符串的指针进行向右移动
if(!numberString.trim().equals(""))
{
i+=numberString.length();
order++;
break;
}
else
{
order++;
}
}while(true);
result+=" "+numberString;
}
}
//将栈中剩余的字符进行弹出
while(!save.isEmpty())
{
result+=" "+save.pop();
}
return result;
}
/**
* 该方法用于设置实例变量input的值
* @param input 为中序表达式
*/
public void setInput(String input)
{
//去掉输入的字符串中存在的所有的空字符
input=input.replaceAll(" ", "");
//去掉中序表达式字符串中各种杂七杂八的字符
input=input.replaceAll("[^+\\-*/×÷\\.\\d()()]", "");
this.input=input;
}
//该方法用于实现一个符号表
private static Map<String,Integer> getMap()
{
Map<String,Integer> temp=new HashMap<String,Integer>();
//定义各个运算符的优先级,其中,x和÷字符用于兼容
temp.put("*", 2);
temp.put("/", 2);
temp.put("×",2);
temp.put("÷",2);
temp.put("+", 1);
temp.put("-",1);
return temp;
}
}
计算后序表达式的结果:
计算一个后序表达式的值比较简单,只要先找到运算符,再去找前面最后出现的两个操作数,从而构成一个较小的算术表达式进行运算,在计算过程中,也需要利用一个栈来保留后序表达式中还未参与运算的操作数,此栈称之为操作数栈。计算后序表达式值的算法的基本实现如下:
- 初始化一个操作数栈
- 从左到右依次读入后序表达式中的字符:
- 若当前字符是操作数,则压入操作数栈中
- 若当前字符是运算符,则从栈顶弹出两个操作数并分别作为第2个操作数和第1个操作数参与运算,再将运算结果压入操作数栈中
- 重复步骤2,直到读入的后序表达式结束为止,则操作数栈中的栈顶元素即为后序表达式的计算结果
其实现的关键代码如下:
/**
* 该方法用于将后序表达式的结果进行计算,得出其最终结果
* 返回最终计算的结果。
* 输入的参数为前面带空格的,数字和运算符之间带空格的后序表达式
*
*/
public double counter(String input)
{
//去掉其首尾空格
input=input.trim();
//该栈用于存储下数字
Stack<String> numbers=new Stack<String>();
//用于记录下数字使用
String n="";
//用于循环遍历每一个字符
for(int i=0;i<input.length();i++)
{
String ch=String.valueOf(input.charAt(i));
//当前的字符为空字符的时候,遍历下一个字符
if(ch.equals(" "))
{
//将数字字符串存入栈中并遍历下一个字符
numbers.push(n);
n="";
continue;
}
//当为数字的时候,对其进行组装,将其组装成字符串
else if(ch.matches("[0-9\\.]"))
{
n+=ch;
}
//当其为运算符的时候,对其栈中的前两个数字字符串进行弹出,并将运算结果压入栈中
else
{
switch(ch)
{
case "×":
case "*":
{
double two=Double.parseDouble(numbers.pop());
double one=Double.parseDouble(numbers.pop());
double result=one*two;
n=String.valueOf(result);
break;
}
case "÷":
case "/":
{
double two=Double.parseDouble(numbers.pop());
double one=Double.parseDouble(numbers.pop());
double result=one/two;
n=String.valueOf(result);
break;
}
case "+":
{
double two=Double.parseDouble(numbers.pop());
double one=Double.parseDouble(numbers.pop());
double result=one+two;
n=String.valueOf(result);
break;
}
case "-":
{
double two=Double.parseDouble(numbers.pop());
double one=Double.parseDouble(numbers.pop());
double result=one-two;
n=String.valueOf(result);
break;
}
}
}
}
//将最后计算的结果放入栈中
numbers.push(n);
double result=Double.parseDouble(numbers.pop());
return result;
}
K:逆波兰算法的更多相关文章
- C++ | 栈的应用(逆波兰算法) | 计算器
#include <iostream> using std::cin; using std::cout; using std::endl; template<typename T&g ...
- 算法--链表的K逆序问题
转载请标明出处http://www.cnblogs.com/haozhengfei/p/9e6f4dda3138cf9fab17f996ec85b624.html 链表的K逆序问题 链表的k逆 ...
- shunting-yard 调度场算法、中缀表达式转逆波兰表达式
中缀表达式 1*(2+3) 这就是一个中缀表达式,运算符在数字之间,计算机处理前缀表达式和后缀表达式比较容易,但处理中缀表达式却不太容易,因此,我们需要使用shunting-yard Algorith ...
- leetcode算法学习----逆波兰表达式求值(后缀表达式)
下面题目是LeetCode算法:逆波兰表达式求值(java实现) 逆波兰表达式即后缀表达式. 题目: 有效的运算符包括 +, -, *, / .每个运算对象可以是整数,也可以是另一个逆波兰表达式.同 ...
- 代码随想录第十三天 | 150. 逆波兰表达式求值、239. 滑动窗口最大值、347.前 K 个高频元素
第一题150. 逆波兰表达式求值 根据 逆波兰表示法,求表达式的值. 有效的算符包括 +.-.*./ .每个运算对象可以是整数,也可以是另一个逆波兰表达式. 注意 两个整数之间的除法只保留整数部分. ...
- C++基础算法学习——逆波兰表达式问题
例题:逆波兰表达式逆波兰表达式是一种把运算符前置的算术表达式,例如普通的表达式2 + 3的逆波兰表示法为+ 2 3.逆波兰表达式的优点是运算符之间不必有优先级关系,也不必用括号改变运算次序,例如(2 ...
- ACM -- 算法小结(六)逆波兰表达式
逆波兰表达式 //问题描述:逆波兰表达式是一种把运算符前置的算术表达式,例如普通的表达式2+3的 //逆波兰表达式法为+ 2 3.逆波兰表达式的优点是运算符之间不必有优先级关系,也不必 //用括号改 ...
- C#数据结构与算法系列(十):逆波兰计算器——逆波兰表达式(后缀表达式)
1.介绍 后缀表达式又称逆波兰表达式,与前缀表达式相似,只是运算符位于操作数之后 2.举例说明 (3+4)*5-6对应的后缀表达式就是3 4 +5 * 6 - 3.示例 输入一个逆波兰表达式(后缀表达 ...
- noi1696 逆波兰表达式
1696:逆波兰表达式 http://noi.openjudge.cn/ch0303/1696/ 总时间限制: 1000ms 内存限制: 65536kB 描述 逆波兰表达式是一种把运算符前置的算术 ...
随机推荐
- React Native vs. Cordova.
简评:跨平台开发五彩斑斓,本文作者简单介绍 RN 和 Cordova 的两种不同终端跨平台流程,从与原生开发联系中分析其利弊. 什么是原生(Native)? 原生是一个相对概念.其实软件开发最后意义指 ...
- 5. STL编程五
1. STL的六大组件: 容器(Container) 算法(Algorithm) 迭带器(Iterator) 仿函数(Function object) 适配器(Adaptor) 空间配置器(alloc ...
- JSP页面开发知识点整理
刚学JSP页面开发,把知识点整理一下. ----------------------------------------------------------------------- JSP语法htt ...
- linux下安装gcc详解
1.了解一下gcc 目前,GCC可以用来编译C/C++.FORTRAN.JAVA.OBJC.ADA等语言的程序,可根据需要选择安装支持的语言.我自己linux上是4.1.2版本,是不支持openMP的 ...
- Python基础部分的疑惑解析(2)
变量: 变量名由 字母.数字.下划线构成,数字不能做为开头 不能用关键字:另外一些内置的方法也别用 推荐使用下划线命名间两个单词user_id 变量在最后底层处理的时候没什么意义,但是在命名的时候有利 ...
- 基础概念——令人迷惑的EOF
EOF概念常常使人迷惑. 首先我们要理解并没有像EOF字符这样的东西. 进一步讲EOF是由内核检测到的一种条件. 应用程序在它接收到由read函数返回的零返回码时,它就会发现EOF条件. 对于磁盘文件 ...
- dwz+ssh Http status: 200 OK
问题描述,用超链接跳转页面的时候报这个错,原因是超链接的target没有设置对, 跳转页面应该用 target="navTab", 我原先用 target="navTab ...
- 分布问题(二元,多元变量分布,Beta,Dir)
这涉及到数学的概率问题. 二元变量分布: 伯努利分布,就是0-1分布(比如一次抛硬币,正面朝上概率) 那么一次抛硬币的概率分布如下: 假设训练数据如下: 那么根据最大似然估计(MLE),我 ...
- dp--2019南昌网络赛B-Match Stick Game
dp--2019南昌网络赛B-Match Stick Game Xiao Ming recently indulges in match stick game and he thinks he is ...
- Mac安装的PyCharm找不到顶部菜单栏 PyCharm找不到setting PyCharm不能个性化设置和直接导库
安装的是最新版的PyCharm,打开发现没有顶部菜单栏,不能直接导库..有点方 以前的就是下面这种 找了很久发现原来在右下角!!!眼拙 点击画圈圈的地方就可以直接进去导库这些啦〜