前面我们介绍了三种数据结构,第一种数组主要用作数据存储,但是后面的两种栈和队列我们说主要作为程序功能实现的辅助工具,其中在介绍栈时我们知道栈可以用来做单词逆序,匹配关键字符等等,那它还有别的什么功能吗?以及数据结构与本篇博客的主题前缀、中缀、后缀表达式有什么关系呢?

1、人如何解析算术表达式

  如何解析算术表达式?或者换种说法,遇到某个算术表达式,我们是如何计算的:

  ①、求值 3+4-5

  

  这个表达式,我们在看到3+4后都不能直接计算3+4的值,知道看到4后面的 - 号,因为减号的优先级和前面的加号一样,所以可以计算3+4的值了,如果4后面是 * 或者 /,那么就要在乘除过后才能做加法操作,比如:

  ②、求值 3+4*5

  

  这个不能先求3+4的值,因为4后面的*运算级别比前面的+高。通过这两个表达式的说明,我们可以总结解析表达式的时候遵循的几条规则:

  ①、从左到右读取算式。

  ②、已经读到了可以计算值的两个操作数和一个操作符时,可以计算,并用计算结果代替那两个操作数和一个操作符。

  ③、继续这个过程,从左到右,能算就算,直到表达式的结尾。

2、计算机如何解析算术表达式

  对于前面的表达式 3+4-5,我们人是有思维能力的,能根据操作符的位置,以及操作符的优先级别能算出该表达式的结果。但是计算机怎么算?

  计算机必须要向前(从左到右)来读取操作数和操作符,等到读取足够的信息来执行一个运算时,找到两个操作数和一个操作符进行运算,有时候如果后面是更高级别的操作符或者括号时,就必须推迟运算,必须要解析到后面级别高的运算,然后回头来执行前面的运算。我们发现这个过程是极其繁琐的,而计算机是一个机器,只认识高低电平,想要完成一个简单表达式的计算,我们可能要设计出很复杂的逻辑电路来控制计算过程,那更不用说很复杂的算术表达式,所以这样来解析算术表达式是不合理的,那么我们应该采取什么办法呢?

  请大家先看看什么是前缀表达式,中缀表达式,后缀表达式:这三种表达式其实就是算术表达式的三种写法,以 3+4-5为例

  ①、前缀表达式:操作符在操作数的前面,比如 +-543

  ②、中缀表达式:操作符在操作数的中间,这也是人类最容易识别的算术表达式 3+4-5

  ③、后缀表达式:操作符在操作数的后面,比如 34+5-

  上面我们讲的人是如何解析算术表达式的,也就是解析中缀表达式,这是人最容易识别的,但是计算机不容易识别,计算机容易识别的是前缀表达式和后缀表达式,将中缀表达式转换为前缀表达式或者后缀表达式之后,计算机能很快计算出表达式的值,那么中缀表达式是如何转换为前缀表达式和后缀表达式,以及计算机是如何解析前缀表达式和后缀表达式来得到结果的呢?

3、后缀表达式

  后缀表达式,指的是不包含括号,运算符放在两个运算对象的后面,所有的计算按运算符出现的顺序,严格从左向右进行(不再考虑运算符的优先规则)。

  由于后缀表达式的运算符在两个操作数的后面,那么计算机在解析后缀表达式的时候,只需要从左向右扫描,也就是只需要向前扫描,而不用回头扫描,遇到运算符就将运算符放在前面两个操作符的中间(这里先不考虑乘方类似的单目运算),一直运算到最右边的运算符,那么就得出运算结果了。既然后缀表达式这么好,那么问题来了:

  ①、如何将中缀表达式转换为后缀表达式?

  对于这个问题,转换的规则如下:

  

  一、先自定义一个栈

  1. package com.ys.poland;
  2.  
  3. public class MyCharStack {
  4. private char[] array;
  5. private int maxSize;
  6. private int top;
  7.  
  8. public MyCharStack(int size){
  9. this.maxSize = size;
  10. array = new char[size];
  11. top = -1;
  12. }
  13.  
  14. //压入数据
  15. public void push(char value){
  16. if(top < maxSize-1){
  17. array[++top] = value;
  18. }
  19. }
  20.  
  21. //弹出栈顶数据
  22. public char pop(){
  23. return array[top--];
  24. }
  25.  
  26. //访问栈顶数据
  27. public char peek(){
  28. return array[top];
  29. }
  30.  
  31. //查看指定位置的元素
  32. public char peekN(int n){
  33. return array[n];
  34. }
  35.  
  36. //为了便于后面分解展示栈中的内容,我们增加了一个遍历栈的方法(实际上栈只能访问栈顶元素的)
  37. public void displayStack(){
  38. System.out.print("Stack(bottom-->top):");
  39. for(int i = 0 ; i < top+1; i++){
  40. System.out.print(peekN(i));
  41. System.out.print(' ');
  42. }
  43. System.out.println("");
  44. }
  45.  
  46. //判断栈是否为空
  47. public boolean isEmpty(){
  48. return (top == -1);
  49. }
  50.  
  51. //判断栈是否满了
  52. public boolean isFull(){
  53. return (top == maxSize-1);
  54. }
  55.  
  56. }

  二、前缀表达式转换为后缀表达式

  1. package com.ys.poland;
  2.  
  3. public class InfixToSuffix {
  4. private MyCharStack s1;//定义运算符栈
  5. private MyCharStack s2;//定义存储结果栈
  6. private String input;
  7.  
  8. //默认构造方法,参数为输入的中缀表达式
  9. public InfixToSuffix(String in){
  10. input = in;
  11. s1 = new MyCharStack(input.length());
  12. s2 = new MyCharStack(input.length());
  13. }
  14. //中缀表达式转换为后缀表达式,将结果存储在栈中返回,逆序显示即后缀表达式
  15. public MyCharStack doTrans(){
  16. for(int j = 0 ; j < input.length() ; j++){
  17. System.out.print("s1栈元素为:");
  18. s1.displayStack();
  19. System.out.print("s2栈元素为:");
  20. s2.displayStack();
  21. char ch = input.charAt(j);
  22. System.out.println("当前解析的字符:"+ch);
  23. switch (ch) {
  24. case '+':
  25. case '-':
  26. gotOper(ch,1);
  27. break;
  28. case '*':
  29. case '/':
  30. gotOper(ch,2);
  31. break;
  32. case '(':
  33. s1.push(ch);//如果当前字符是'(',则将其入栈
  34. break;
  35. case ')':
  36. gotParen(ch);
  37. break;
  38. default:
  39. //1、如果当前解析的字符是操作数,则直接压入s2
  40. //2、
  41. s2.push(ch);
  42. break;
  43. }//end switch
  44. }//end for
  45.  
  46. while(!s1.isEmpty()){
  47. s2.push(s1.pop());
  48. }
  49. return s2;
  50. }
  51.  
  52. public void gotOper(char opThis,int prec1){
  53. while(!s1.isEmpty()){
  54. char opTop = s1.pop();
  55. if(opTop == '('){//如果栈顶是'(',直接将操作符压入s1
  56. s1.push(opTop);
  57. break;
  58. }else{
  59. int prec2;
  60. if(opTop == '+' || opTop == '-'){
  61. prec2 = 1;
  62. }else{
  63. prec2 = 2;
  64. }
  65. if(prec2 < prec1){//如果当前运算符比s1栈顶运算符优先级高,则将运算符压入s1
  66. s1.push(opTop);
  67. break;
  68. }else{//如果当前运算符与栈顶运算符相同或者小于优先级别,那么将S1栈顶的运算符弹出并压入到S2中
  69. //并且要再次再次转到while循环中与 s1 中新的栈顶运算符相比较;
  70. s2.push(opTop);
  71. }
  72. }
  73.  
  74. }//end while
  75. //如果s1为空,则直接将当前解析的运算符压入s1
  76. s1.push(opThis);
  77. }
  78.  
  79. //当前字符是 ')' 时,如果栈顶是'(',则将这一对括号丢弃,否则依次弹出s1栈顶的字符,压入s2,直到遇到'('
  80. public void gotParen(char ch){
  81. while(!s1.isEmpty()){
  82. char chx = s1.pop();
  83. if(chx == '('){
  84. break;
  85. }else{
  86. s2.push(chx);
  87. }
  88. }
  89. }
  90.  
  91. }

  三、测试

  1. @Test
  2. public void testInfixToSuffix(){
  3. String input;
  4. System.out.println("Enter infix:");
  5. Scanner scanner = new Scanner(System.in);
  6. input = scanner.nextLine();
  7. InfixToSuffix in = new InfixToSuffix(input);
  8. MyCharStack my = in.doTrans();
  9. my.displayStack();
  10. }

  四、结果

  

  五、分析

  

  ②、计算机如何实现后缀表达式的运算?

  

  1. package com.ys.poland;
  2.  
  3. public class CalSuffix {
  4. private MyIntStack stack;
  5. private String input;
  6.  
  7. public CalSuffix(String input){
  8. this.input = input;
  9. stack = new MyIntStack(input.length());
  10.  
  11. }
  12.  
  13. public int doCalc(){
  14. int num1,num2,result;
  15. for(int i = 0 ; i < input.length() ; i++){
  16. char c = input.charAt(i);
  17. if(c >= '0' && c <= '9'){
  18. stack.push((int)(c-'0'));//如果是数字,直接压入栈中
  19. }else{
  20. num2 = stack.pop();//注意先出来的为第二个操作数
  21. num1 = stack.pop();
  22. switch (c) {
  23. case '+':
  24. result = num1+num2;
  25. break;
  26. case '-':
  27. result = num1-num2;
  28. break;
  29. case '*':
  30. result = num1*num2;
  31. break;
  32. case '/':
  33. result = num1/num2;
  34. break;
  35. default:
  36. result = 0;
  37. break;
  38. }//end switch
  39.  
  40. stack.push(result);
  41. }//end else
  42. }//end for
  43. result = stack.pop();
  44. return result;
  45. }
  46.  
  47. public static void main(String[] args) {
  48. //中缀表达式:1*(2+3)-5/(2+3) = 4
  49. //后缀表达式:123+*123+/-
  50. CalSuffix cs = new CalSuffix("123+*523+/-");
  51. System.out.println(cs.doCalc()); //4
  52. }
  53.  
  54. }

  

4、前缀表达式

  前缀表达式,指的是不包含括号,运算符放在两个运算对象的前面,严格从右向左进行(不再考虑运算符的优先规则),所有的计算按运算符出现的顺序。

  注意:后缀表达式是从左向右解析,而前缀表达式是从右向左解析。

  ①、如何将中缀表达式转换为前缀表达式?

  

  ②、计算机如何实现前缀表达式的运算?

  

  

参考文档:http://blog.csdn.net/antineutrino/article/details/6763722/  

参考书籍:《Java数据结构和算法》  

Java数据结构和算法(六)——前缀、中缀、后缀表达式的更多相关文章

  1. Java数据结构和算法(六)--二叉树

    什么是树? 上面图例就是一个树,用圆代表节点,连接圆的直线代表边.树的顶端总有一个节点,通过它连接第二层的节点,然后第二层连向更下一层的节点,以此递推 ,所以树的顶端小,底部大.和现实中的树是相反的, ...

  2. Java数据结构和算法(七)--AVL树

    在上篇博客中,学习了二分搜索树:Java数据结构和算法(六)--二叉树,但是二分搜索树本身存在一个问题: 如果现在插入的数据为1,2,3,4,5,6,这样有序的数据,或者是逆序 这种情况下的二分搜索树 ...

  3. Java数据结构和算法 - 栈和队列

    Q: 栈.队列与数组的区别? A: 本篇主要涉及三种数据存储类型:栈.队列和优先级队列,它与数组主要有如下三个区别: A: (一)程序员工具 数组和其他的结构(栈.队列.链表.树等等)都适用于数据库应 ...

  4. Java数据结构和算法(五)——队列

    队列.queue,就是现实生活中的排队. 1.简单队列: public class Queqe { private int array[]; private int front; private in ...

  5. Java数据结构和算法 - 二叉树

    前言 数据结构可划分为线性结构.树型结构和图型结构三大类.前面几篇讨论了数组.栈和队列.链表都是线性结构.树型结构中每个结点只允许有一个直接前驱结点,但允许有一个以上直接后驱结点.树型结构有树和二叉树 ...

  6. Java数据结构和算法 - 递归

    三角数字 Q: 什么是三角数字? A: 据说一群在毕达哥拉斯领导下工作的古希腊的数学家,发现了在数学序列1,3,6,10,15,21,……中有一种奇特的联系.这个数列中的第N项是由第N-1项加N得到的 ...

  7. Java数据结构和算法

    首先,本人自学java,但是只学习了java的基础知识,所以想接下来学习一下数据结构和算法,但是找了很多教材,大部分写的好的都是用c语言实现的,虽然知道数据结构和算法,跟什么语言实现的没有关系,但是我 ...

  8. 【Java数据结构学习笔记之二】Java数据结构与算法之栈(Stack)实现

      本篇是java数据结构与算法的第2篇,从本篇开始我们将来了解栈的设计与实现,以下是本篇的相关知识点: 栈的抽象数据类型 顺序栈的设计与实现 链式栈的设计与实现 栈的应用 栈的抽象数据类型   栈是 ...

  9. Java数据结构和算法(十四)——堆

    在Java数据结构和算法(五)——队列中我们介绍了优先级队列,优先级队列是一种抽象数据类型(ADT),它提供了删除最大(或最小)关键字值的数据项的方法,插入数据项的方法,优先级队列可以用有序数组来实现 ...

随机推荐

  1. Assignments

    Assignments Time Limit: 4000/2000 MS (Java/Others)    Memory Limit: 32768/32768 K (Java/Others) Tota ...

  2. Centos7下部署ceph 12.2.1 (luminous)集群及RBD使用

    前言 本文搭建了一个由三节点(master.slave1.slave2)构成的ceph分布式集群,并通过示例使用ceph块存储. 本文集群三个节点基于三台虚拟机进行搭建,节点安装的操作系统为Cento ...

  3. vue初级学习--控制台创建vue项目

    一.导语 困困的我,好想睡啊,但还是敲下这边有的没的的笔记吧 二.正文 1.输入 vue list 可查看此环境下,vue能创建的项目模板 没将webpack.vue-route啃精了的话,老实用we ...

  4. 和为S的两个数

    题目 输入一个递增排序的数组和一个数字S,在数组中查找两个数,是的他们的和正好是S,如果有多对数字的和等于S,输出两个数的乘积最小的. 输出描述: 对应每个测试案例,输出两个数,小的先输出. 思考 注 ...

  5. 关于隐藏元素高度的问题 css visibility:hidden 与 display:none的区别

    其实这是一个老问题了,s visibility:hidden 与 display:none 共同点就是都会似的元素不可见.但是 visibility:hidden 的DOM元素是占用空间的,会挤占其他 ...

  6. JavaScript正则表达式知识点

    通过学习imooc课程<JavaScript正则表达式>http://www.imooc.com/video/12539,对视频教学内容做一个知识整理. 一个正则表达式在线工具:http: ...

  7. ldap数据库--ODSEE--复制协议

    简单介绍一下ODSEE的复制拓扑的建立,复制协议可以通过管理界面进行创建,也可以通过命令行创建.在此之前需要了解一些复制协议的相关概念,这里针对OESEE. 1,复制角色 master(提供者,也可以 ...

  8. 二叉搜索树 (BST) 的创建以及遍历

    二叉搜索树(Binary Search Tree) : 属于二叉树,其中每个节点都含有一个可以比较的键(如需要可以在键上关联值), 且每个节点的键都大于其左子树中的任意节点而小于右子树的任意节点的键. ...

  9. C# 4动态编程新特性与DLR剖析

    =================================================== 注:很久没有发文了,贴一篇新文吧.从Word直接贴过来的,没仔细排版,诸位海涵.有关DLR和C# ...

  10. [转载] redis 的两种持久化方式及原理

    转载自http://www.m690.com/archives/371 Redis是一种高级key-value数据库.它跟memcached类似,不过数据可以持久化,而且支持的数据类型很丰富.有字符串 ...