Chinaunix首页 | 论坛 | 博客
  • 博客访问: 1547119
  • 博文数量: 327
  • 博客积分: 10000
  • 博客等级: 上将
  • 技术积分: 3556
  • 用 户 组: 普通用户
  • 注册时间: 2005-04-05 21:28
个人简介

东黑布衣,流浪幽燕。 真诚善良,值得信赖。

文章分类

全部博文(327)

我的朋友

分类: BSD

2017-06-12 12:00:40

逆波兰式又叫做后缀表达式。在通常的表达式中,二元运算符总是置于与之相关的两个运算对象之间,这种表示法也称为中缀表示。波兰逻辑学家J.Lukasiewicz于1929年提出了另一种表示表达式的方法,按此方法,每一运算符都置于其运算对象之后,故称为后缀表达式。

正常的表达式 逆波兰式
a+b ---> a,b,+
a+(b-c) ---> a,b,c,-,+
a+(b-c)*d ---> a,b,c,-,d,*,+
a+d*(b-c)--->a,d,b,c,-,*,+
a=1+3 ---> a=1,3 +
http=(smtp+http+telnet)/1024 写成什么呢?
http,smtp,http,+,telnet,+,1024,/,=
用途
逆波兰表达式是一种十分有用的表达式,它将复杂表达式转换为可以依靠简单的操作得到计算结果的表达式。例如(a+b)*(c+d)转换为ab+cd+*
优势
它的优势在于只用两种简单操作,入栈和出栈就可以搞定任何普通表达式的运算。其运算方式如下:
如果当前字符为变量或者为数字,则压栈,如果是运算符,则将栈顶两个元素弹出作相应运算,结果再入栈,最后当表达式扫描完后,栈里的就是结果。
算法
中缀表达式转后缀表达式的方法:
1.遇到操作数:直接输出(添加到后缀表达式中)
2.栈为空时,遇到运算符,直接入栈
3.遇到左括号:将其入栈
4.遇到右括号:执行出栈操作,并将出栈的元素输出,直到弹出栈的是左括号,左括号不输出。
5.遇到其他运算符:加减乘除:弹出所有优先级大于或者等于该运算符的栈顶元素,然后将该运算符入栈
6.最终将栈中的元素依次出栈,输出。
例如
a+b*c+(d*e+f)*g ----> abc*+de*f+g*+

遇到a:直接输出:
后缀表达式:a
堆栈:空

遇到+:堆栈:空,所以+入栈
后缀表达式:a
堆栈:+
遇到b: 直接输出
后缀表达式:ab
堆栈:+
遇到*:堆栈非空,但是+的优先级不高于*,所以*入栈
后缀表达式: ab
堆栈:*+
遇到c:直接输出
后缀表达式:abc
堆栈:*+
遇到+:堆栈非空,堆栈中的*优先级大于+,输出并出栈,堆栈中的+优先级等于+,输出并出栈,然后再将该运算符(+)入栈
后缀表达式:abc*+
堆栈:+
遇到(:直接入栈
后缀表达式:abc*+
堆栈:(+
遇到d:输出
后缀表达式:abc*+d
堆栈:(+
遇到*:堆栈非空,堆栈中的(优先级小于*,所以不出栈
后缀表达式:abc*+d
堆栈:*(+
遇到e:输出
后缀表达式:abc*+de
堆栈:*(+
遇到+:由于*的优先级大于+,输出并出栈,但是(的优先级低于+,所以将*出栈,+入栈
后缀表达式:abc*+de*
堆栈:+(+
遇到f:输出
后缀表达式:abc*+de*f
堆栈:+(+
遇到):执行出栈并输出元素,直到弹出左括号,所括号不输出
后缀表达式:abc*+de*f+
堆栈:+
遇到*:堆栈为空,入栈
后缀表达式: abc*+de*f+
堆栈:*+
遇到g:输出
后缀表达式:abc*+de*f+g
堆栈:*+
遇到中缀表达式结束:弹出所有的运算符并输出
后缀表达式:abc*+de*f+g*+
堆栈:空

[求值]
逆波兰表达式是一种利用栈来进行运算的数学表达式。

以一个表达式 1 + 2 * 3 为例。以顺序方式输入这个表达式,计算机首先
需要解析这个表达式,然后递归的求值。比如从左起进行顺序解析,得到
一个符号树:
   +
  / \
 1   *
    / \
   2   3
计算机会递归的从叶子开始求值,最后回到根,得出结果。所以对于一个
比较复杂的表达式,树可能会很深,而且递归的过程将会消耗大量的内存,
由于有解析和运算两个过程,时间开销也不理想。

如果将上式改为逆波兰表达式: 3 2 * 1 +
那么就能实现“在线处理”。在线处理是这类问题在计算机中最快的算法。
还是从左侧开始,扫描这个表达式。扫描到第一项,为一个操作数,那么
可以把这个数压栈,然后继续扫面。
top:
 3

第二项还是一个操作数,同样的压栈继续。
top:
 2
 3

到第三项,得到一个双目运算符,这时候计算机就从操作数栈中弹出两个
数作为运算符的两个参数,然后进行运算,并将结果再压回操作数栈。
top:
 6

接着第四项,一个操作数,压栈。第五项,又是一个双目运算符,那么弹
出两个操作数进行运算,把结果压栈。
top:
 1
 6

到此,这个表达式就扫描结束了,操作数栈中最后会剩下表达式的运算结果。
top:
 7

不需要两次操作,只要从头扫描到尾就能求出结果,也不需要递归,只需
要一个很小的栈就可以。所以逆波兰表达式算法取得了时间复杂度和空间
复杂度上的双重优势。

并且:
   从左到右扫描,有运算符就运算,对于复杂的表达式,不容易出错;
   栈可以自动的记录中间结果;
   机器只需要维护一个操作数堆栈,栈中只有操作数和结果

所以在机器上实现起来非常的方便。早期,处理器性能比较弱的时候,使
用这种方式就可以在性能不足的机器上实现任意复杂度的表达式运算。很
棒吧。


      引刀成一块,不负少年头。


[Problem]
Create a program which converts a character string of
calculation formula to a postfix expression and calcu
lates it.

For example, a character string “3+(4+5)*6+7” can be
converted to a postfix expression "345+6*+7+".
The calculation should yield 64.
The operators used in the formula are ‘+’ and ‘*’. Pa
rentheses can be used in the formula. The operands ar
e the integers between 0 ~ 9. The parentheses are alw
ays paired properly.
 
[Input]
The first line of the input file provides the length
of the test case. The test cases are given in the nex
t lines. Total of 10 test cases are given.


  1. 10
    113
    (9+(5*2+1)+(3*3*7*6*9*1*7+1+8*6+6*1*1*5*2)*4*7+4*3*8*2*6+(7*8*4*5)+3+7+(2+6+5+1+7+6+7*3*(6+2)+6+6)*2+4+2*2+4*9*3)
    85
    (4+8+4*(8*5*(7*(6*8)+3+(6+(3+7+1*7*5*4)*3)*2*3+5)+6+7*7)*4+2+9*4+7+2*3*(7*6*1*8)+9+9)
    97
    (9*5+7+8+6+3+9*2+1+7+4+3+9*3*1+4*(4+4*3*1+9*3+(9*5)*1*7*8+2+8+8*7+9*4*9)+(1+1*8+8*9*7+1*4*5*2*5))
    89
    ((3*1*4+6*3+8+4+5+4+2*1+5+3*4)*1+1+(3*2*2+9+5*4*6*9+9+4+1*8+9)*4*8+9+3*7+9*6*9*5+8+3*8+1)
    125
    (2+(6*5+6+5+3*9+6+2+8*2+2)+6+2*2+2*5*1*2+1*8+1*(4+7*5+8+9+7+3*8*5)+(2+9+5*4*4+1+3*9*6*4*5+(5*(3+4)*9+8+7+9*2)+7+7+2)+8+2+7+5)
    113
    (8+8*6+3*9*8*7*6*3+5*(7*6*6+3*5+2*4*9*3+5+2+1*4)*1*7+6*8+9+3+2+8*3+8*(2+6*9+2*2*7+8*1*2+9*3+1+5)*(5*8+4*1*2*4*2))
    115
    (7+9*2+6+5*7+1*7*(9+8+6)*1*2+7+5*9*6*3+4*8*9*6*1*3+7*1+2+5+1*4*9+6*4+7*1*2*4*2+3+((3*4+9+7*1+7+5+3*7*1*7+8*3*8)+7))
    99
    (9*4+(1+5*7*8+9+1+2)+5+(6*(7+4*5*2+4+8*4+7)+9)*1*3*1+1*2*8+3+(2+9*(1*5*9+7*1+1+7+3*2))+1+3*7*8+9*6)
    75
    (2*2+((7+8*8*6+(6*6)*7+7*1)*5)*7+3+1*5+1*8*4+(9+6+(7*5+3+1*8*8*9+4+7+9)*3))
    117
    (8+6*9+2*3+4+2+(6+9+3+7*5*1+2+2+2)*9+4+6*1+6*4+7+7*2+5+2*6*(8*9*8*6+4*2)*5+5*8*3+(6+1+3+3*8*1*2*1+5+6)+1+5+4*7*1*3+1

[Output]
The output file outputs the test case number followin
g the ‘#’ symbol. It is followed by a space, and then
the answer.

  1. #1 672676
    #2 1974171
    #3 12654
    #4 38756
    #5 4035
    #6 155304
    #7 6964
    #8 2819
    #9 24711
    #10 208785

栈实现的代码

  1. #ifndef __Z_STK_H__
  2. #define __Z_STK_H__

  3. #define STK_SZ 1024
  4. typedef struct stack{
  5.    int top;
  6.    int nd[STK_SZ];
  7. }Stack;

  8. int init(Stack *s);
  9. int bempty(Stack *s);
  10. int isfull(Stack *s);
  11. int push(Stack *s,int n);
  12. int pop(Stack *s);
  13. int clear(Stack *s);
  14. int size(Stack *s);
  15. int top(Stack *s);

  16. #endif //Z_STK_H


  17. #include"stdafx.h"
  18. #include <stdio.h>
  19. #include "z_stk.h"

  20. int init(Stack *p)
  21. {
  22.    int i=0;
  23.    if(NULL==p)
  24.       return -1;
  25.    else{
  26.       p->top=0;
  27.       for(i=0;i<STK_SZ;i++)
  28.          p->nd[i]=0;
  29.       return 1;
  30.    }
  31. }

  32. int bempty(Stack *s)
  33. {
  34.    return (0==s->top);
  35. }

  36. int isfull(Stack *s)
  37. {
  38.    return (STK_SZ==s->top);
  39. }

  40. int push(Stack *s,int n)
  41. {
  42.    if(s->top >= STK_SZ)
  43.       return 0;
  44.    s->nd[s->top]=n;
  45.    s->top++;
  46.    return 1;
  47. }

  48. int pop(Stack *s)
  49. {
  50.    int tmp;
  51.    if(s->top==0)
  52.       return 0;
  53.    s->top--;
  54.    tmp=s->nd[s->top];
  55.    s->nd[s->top]=0;
  56.    return tmp;
  57. }

  58. int size(Stack *s)
  59. {
  60.    return s->top;
  61. }

  62. int clear(Stack *s)
  63. {
  64.    s->top=0;
  65.    return 1;
  66. }


  67. int top(Stack *s)
  68. {
  69.    return s->nd[s->top-1];
  70. }

主程序的代码
  1. #include "stdafx.h"
  2. #include <stdio.h>
  3. #include <string.h>
  4. #include "z_stk.h"
  5. #define true 1
  6. #define false 0
  7. char ifix[128];
  8. char pfix[128];

  9. int boperator(char ch)
  10. {
  11.     int i;
  12.    char ops[] = "+-*/";
  13.    for (i = 0; i < sizeof(ops) / sizeof(char); i++)
  14.    {
  15.        if (ch == ops[i])
  16.            return true;
  17.    }
  18.    return false;
  19. }

  20. ///////////////////////////
  21. // 比较两个操作符的优先级
  22. int precedence(char op1, char op2)
  23. {
  24.    if (op1 == '(')
  25.    {
  26.       return -1;
  27.    }

  28.    if (op1 == '+' || op1 == '-')
  29.    {
  30.       if (op2 == '*' || op2 == '/')
  31.       {
  32.          return -1;
  33.       }
  34.       else
  35.       {
  36.          return 0;
  37.       }
  38.    }

  39.    if (op1 == '*' || op1 == '/')
  40.    {
  41.       if (op2 == '+' || op2 == '-')
  42.       {
  43.          return 1;
  44.       }
  45.       else
  46.       {
  47.          return 0;
  48.       }
  49.    }
  50.    return -1;
  51. }

  52. ///////////////////////////////
  53. // 中缀表达式转换成后缀表达式
  54. void ifix2pfix()
  55. {
  56.    int j = 0, len, i;
  57.    char c;
  58.    Stack st;
  59.    init(&st);
  60.    len = strlen(ifix);
  61.    
  62.    for (i = 0; i < len; i++)
  63.    {
  64.       c = ifix[i];
  65.       
  66.       if (c == '(')//左括号入栈
  67.           push(&st,c);
  68.       else if (c == ')')//如果是右括号输出到左括号之间的所有字符,左括号不输出
  69.       {
  70.          while (top(&st) != '(')
  71.          {
  72.             pfix[j++] = top(&st);//输出到逆波兰式
  73.             pop(&st);
  74.          }
  75.          pop(&st);//弹出左括号,不输出
  76.       }
  77.       else
  78.       {
  79.          if (!boperator(c))//如果不是运算符直接添加到逆波兰式
  80.              pfix[j++]=c;
  81.          else//运算符
  82.          {
  83.             /* 这两种写法都可以, 这种看着清晰一些, 下面的那种看着简练一些
  84.             if(bempty(&st)==true)
  85.                push(&st,c);
  86.             else{
  87.                while(precedence(top(&st),c) >= 0){
  88.                   pfix[j++]=top(&st);
  89.                   pop(&st);
  90.                }
  91.                push(&st,c);
  92.             }
  93.             */
  94.             while (bempty(&st) == false && precedence(top(&st), c) >= 0)
  95.             {
  96.                  pfix[j++] = top(&st);
  97.                  pop(&st);
  98.             }
  99.             push(&st,c);
  100.          }
  101.       }
  102.    }

  103.    while (bempty(&st) == false)
  104.    {
  105.       pfix[j++] = top(&st);
  106.       pop(&st);
  107.    }
  108.    pfix[j] = 0;
  109. }

  110. ////////////////////////
  111. // 后缀表达式求值程序
  112. double pfix_eval(void)
  113. {
  114.     int i;
  115.    Stack st;
  116.    init(&st);
  117.    int len = strlen(pfix);
  118.    char c;

  119.    for (i = 0; i < len; i++)
  120.    {
  121.       c = pfix[i];
  122.       if (boperator(c) == false)
  123.       {
  124.          push(&st, c - '0');
  125.       }
  126.       else
  127.       {
  128.          int op1, op2;
  129.          int val;

  130.          op1 = top(&st);
  131.          pop(&st);
  132.          op2 = top(&st);
  133.          pop(&st);

  134.          switch (c)
  135.          {
  136.          case '+':
  137.             val = op1 + op2;
  138.             break;
  139.          case '-':
  140.             val = op2 - op1;
  141.             break;
  142.          case '*':
  143.             val = op1 * op2;
  144.             break;
  145.          case '/':
  146.             val = op2 / op1;
  147.             break;
  148.          }
  149.          push(&st,val);
  150.       }
  151.    }

  152.    return top(&st);
  153. }

  154. int _tmain(int argc, _TCHAR* argv[])
  155. {
  156.    int tc,T;
  157.    int len;
  158.    double val;
  159.    freopen("01calculator.txt","r",stdin);
  160.    scanf("%d",&T);
  161.    for(tc=1;tc<=T;tc++)
  162.    {
  163.       scanf("%d\n",&len);
  164.       memset(ifix,0,128);
  165.       memset(pfix,0,128);
  166.       gets_s(ifix,len+1);
  167.       if (strlen(ifix) == 0)
  168.           continue;

  169.       ifix2pfix();
  170.       val = pfix_eval();
  171.       printf("#%d %.0f\n",tc, val);
  172.    }

  173.    return 0;
  174. }

2017-06-16 上午调试通过。
阅读(1555) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~