• 栈的应用1——超级计算器(中缀与后缀表达式)C语言


    这里要学的程序主要用来实现一个功能——输入表达式输出结果,也就是一个计算器。效果如下:

    这个程序主要有两个步骤:1、把中缀表达式转换为后缀表达式;2、计算后缀表达式的结果。

    首先先明白几个问题:

    1、为什么要转换为后缀表达式?因为后缀表达式容易实现计算机计算结果。(可以百度一下后缀表达式,又称逆波兰式)

    2、怎么把中缀表达式转换为后缀表达式?

    3、怎么用后缀表达式输出结果?

    相信如果弄明白了上面几个问题,有C语言基础的同学就可以编出这个程序啦。而后面两个问题都要用到一个数据结构——栈。实际上数据结构只是一种思想,一种思维,是连接人脑与计算机的桥梁(纯属个人杜撰= =)

      好,那么我们先学怎么把中缀表达式转换为后缀表达式。

      为了简化问题,我们不妨设输入的数字都是一位整数,这样读入的时候只需要以字符的方式逐个读入即可,也就是说读入的必定是数字或者+-*/,读到回车视为结束;另外不妨直接将结果打印到屏幕上,也就是说结果不必存储(这样我们可以更多地关注算法本身,而非输入输出方式的细枝末节)

      类似上面的图片,它的后缀表达式是  9 3 1 - 2 * + 5 2 / +  怎么得到的呢?下面开始讲这个转换的算法:

      1、设置一个栈,栈底填一个"@"作为结束符(当然也可以用别的符号,至于为什么要用一会讲优先级的时候会说);

      2、开始逐个字符读入;

      3、如果读入的是数字,直接打印printf("%c ",c);(数字后面打印一个空格作为间隔符,要不然没法看了931-。。。不解释)

      4、如果读入的是"("直接进栈;

      5、如果读入的是")",说明前面肯定读入过一个"("找到这个左括号,把两者之间的符号逐个弹栈(这里要说明的是,括号不必打印,因为后缀表达式没有括号);

      6、如果不是上面的几种情况,那必定是+-*/中的一个啦,这样来说就容易多了。本来这里应该说优先级的问题,可是我还是想先说说栈。讲到这里,实际上大家应该也看出来了,栈实际上就是一个进栈和出栈的问题,这里也是一样,对于表达式这里我们可以发现,这个栈是用来存储符号的,()+-*/,所以我们只需要明白什么时候符号可以进栈,什么时候符号可以出栈就可以啦~上面已经讲了两个了,左括号的时候直接可以进栈,右括号的时候把两者之间的出栈打印。而对于+-*/,只需要记住一个法则,对于读入的这个符号,只有它比栈顶符号的优先级高的时候才可以进栈(优先级相同也不能进栈),而它不能进栈,就只能让栈顶的出栈啦~所以不断出栈,知道这个符号可以进栈,这个新读入的符号就算处理完成啦。(优先级函数可以参照后面的程序代码)

    OK,按照上面的算法,扫描完一遍读入的中缀表达式,就可以在屏幕上输出后缀表达式啦。下面附上自制代码(C语言,带注释):

     1 #include<stdio.h>
     2 #include<stdlib.h>
     3 #define newp (stype *)malloc(sizeof(stype))  //定义一个申请栈地址的宏
     4 typedef struct _stack{
     5  char dat;
     6     struct _stack *next;
     7 } stype;  //建立栈类型
     8 int tance(char x)  //探测优先级 
     9 {
    10  if(x=='+'||x=='-') return 0;
    11  else if (x=='*'||x=='/') return 1;
    12  else if (x=='@'||x=='('||x==')')  return -1;
    13 }
    14 int main()
    15 {
    16  stype *s,*top;  //栈指针和栈顶指针 
    17  char c;
    18  s=newp;
    19  s->dat='@';
    20  s->next=NULL;
    21  top=s;
    22  c=getchar();  //此后为读取中缀表达式的部分,用字符一个一个的读,直到读到回车 
    23  while(c!='
    ')
    24  {
    25   if (c>='0'&&c<='9')  //如果读入数字,直接打印 
    26   {
    27    printf("%c ",c);
    28   }
    29   else if (c=='(')  //如果是左括号,直接进栈 
    30   {
    31    s=newp;
    32    s->dat=c;
    33    s->next=top;
    34    top=s;
    35   }
    36   else if (c==')')  //如果是右括号,匹配左括号,把两者之间的栈内符号全部弹出 
    37   {
    38    while (top->dat!='(')
    39    {
    40     s=top;
    41     printf("%c ",top->dat);
    42     top=top->next;
    43     free(s);
    44    }
    45    s=top;
    46    top=top->next;
    47    free(s);
    48   }
    49   else  //否则肯定是+-*/了 
    50   {
    51    int a=tance(c);
    52    int b=tance(top->dat);  //比较该符号和栈顶符号的优先级 
    53    if (a>b)  //如果大于直接压进去 
    54    {
    55     s=newp;
    56     s->dat=c;
    57     s->next=top;
    58     top=s;
    59    }
    60    else  //否则就把栈顶的符号一直弹出,直到弹到可以压进去,然后压进去(也就是说等于也不能压进去) 
    61    {
    62     while (a<=b)
    63     {
    64      s=top;
    65         printf("%c ",top->dat);
    66         top=top->next;
    67         free(s);
    68         b=tance(top->dat);
    69     }
    70     s=newp;
    71     s->dat=c;
    72     s->next=top;
    73     top=s;
    74    }
    75   }
    76   c=getchar();  //读取下一个字符 
    77  }
    78  while (top->dat!='@')  //读完和还不算完,还要把栈内剩余的所有符号挨个弹出 
    79  {
    80   s=top;
    81   printf("%c ",top->dat);
    82   top=top->next;
    83   free(s);
    84  }
    85  return 0;  //后缀表达式输出完毕 
    86 }

    好,如果你看懂了上面所有的内容,恭喜你,已经学会一半了。那么,现在我们就可以去掉那个只能输入一位数的大前提,只需要在输入的时候处理一下,就可以实现任意位数的数,甚至是小数(可以参照最后的程序代码)。

    那么现在我们来完成第二步:已知后缀表达式输出结果。这个算法要比上面的转换简单多了,就是栈的基本操作,只不过这里的栈不是用来存储字符的,而是用来存储数字的。

    我们不妨还是假设一位数吧。

    1、如果读入的是数字,直接进栈;

    2、如果是符号,必然是+-*/中的一个,直需要弹出栈顶的两个数,运算,然后再把结果进栈。直至扫描完整个后缀表达式,栈顶就是最终结果。

    如果看懂了上面,我们可以发现,这个过程直接就可以在转换表达式的时候顺便完成,也就是,如果遇到数字,不打印到屏幕上,而是进栈到数字存储栈里;如果有出栈的符号,不用打印到屏幕上,而是弹出数字栈的两个栈顶元素,然后进栈。就OK啦。下面附上输入中缀表达式输出结果的代码(只是将上面的代码中打印的过程换成了其他操作而已):

      1 #include<stdio.h>
      2 #include<stdlib.h>
      3 
      4 #define newp (stype *)malloc(sizeof(stype))  //定义一个申请栈地址的宏 
      5 
      6 typedef struct _stack{
      7     char dat;
      8     struct _stack *next;
      9 } stype;  //建立栈类型 
     10 
     11 int tance(char x)  //探测优先级 
     12 {
     13     if(x=='+'||x=='-') return 0;
     14     else if (x=='*'||x=='/') return 1;
     15     else if (x=='@'||x=='('||x==')')  return -1;
     16 }
     17 
     18 int main()
     19 {
     20     int rs=0;
     21     stype *s,*top;  //栈指针和栈顶指针 
     22     int calc[50],i=0;
     23     char c;
     24     s=newp;
     25     s->dat='@';
     26     s->next=NULL;
     27     top=s;
     28     c=getchar();  //此后为读取中缀表达式的部分,用字符一个一个的读,直到读到回车 
     29     while(c!='
    ')
     30     {
     31         if (c>='0'&&c<='9')  //如果读入数字,直接打印 
     32         {
     33             i++;
     34             calc[i]=c-48;
     35         }
     36         else if (c=='(')  //如果是左括号,直接进栈 
     37         {
     38             s=newp;
     39             s->dat=c;
     40             s->next=top;
     41             top=s;
     42         }
     43         else if (c==')')  //如果是右括号,匹配左括号,把两者之间的栈内符号全部弹出 
     44         {
     45             while (top->dat!='(')
     46             {
     47                 s=top;
     48                 if (top->dat=='+'){
     49                     calc[i-1]=calc[i-1]+calc[i];
     50                     i--;
     51                 }
     52                 else if (top->dat=='-'){
     53                     calc[i-1]=calc[i-1]-calc[i];
     54                     i--;
     55                 }
     56                 else if (top->dat=='*'){
     57                     calc[i-1]=calc[i-1]*calc[i];
     58                     i--;
     59                 }
     60                 else if (top->dat=='/'){
     61                     calc[i-1]=calc[i-1]/calc[i];
     62                     i--;
     63                 }
     64                 
     65                 top=top->next;
     66                 free(s);
     67             }
     68             s=top;
     69             top=top->next;
     70             free(s);
     71         }
     72         else  //否则肯定是+-*/了 
     73         {
     74             int a=tance(c);
     75             int b=tance(top->dat);  //比较该符号和栈顶符号的优先级 
     76             if (a>b)  //如果大于直接压进去 
     77             {
     78                 s=newp;
     79                 s->dat=c;
     80                 s->next=top;
     81                 top=s;
     82             }
     83             else  //否则就把栈顶的符号一直弹出,直到弹到可以压进去,然后压进去(也就是说等于也不能压进去) 
     84             {
     85                 while (a<=b)
     86                 {
     87                     s=top;
     88                 if (top->dat=='+'){
     89                     calc[i-1]=calc[i-1]+calc[i];
     90                     i--;
     91                 }
     92                 else if (top->dat=='-'){
     93                     calc[i-1]=calc[i-1]-calc[i];
     94                     i--;
     95                 }
     96                 else if (top->dat=='*'){
     97                     calc[i-1]=calc[i-1]*calc[i];
     98                     i--;
     99                 }
    100                 else if (top->dat=='/'){
    101                     calc[i-1]=calc[i-1]/calc[i];
    102                     i--;
    103                 }
    104                     top=top->next;
    105                     free(s);
    106                     b=tance(top->dat);
    107                 }
    108                 s=newp;
    109                 s->dat=c;
    110                 s->next=top;
    111                 top=s;
    112             }
    113         }
    114         c=getchar();  //读取下一个字符 
    115     }
    116     while (top->dat!='@')  //读完和还不算完,还要把栈内剩余的所有符号挨个弹出 
    117     {
    118         s=top;
    119                 if (top->dat=='+'){
    120                     calc[i-1]=calc[i-1]+calc[i];
    121                     i--;
    122                 }
    123                 else if (top->dat=='-'){
    124                     calc[i-1]=calc[i-1]-calc[i];
    125                     i--;
    126                 }
    127                 else if (top->dat=='*'){
    128                     calc[i-1]=calc[i-1]*calc[i];
    129                     i--;
    130                 }
    131                 else if (top->dat=='/'){
    132                     calc[i-1]=calc[i-1]/calc[i];
    133                     i--;
    134                 }
    135         top=top->next;
    136         free(s);
    137     }
    138     
    139     
    140     printf("%d
    ",calc[1]);
    141     return 0; 
    142 }

    OK,会了一位数的,其他的也就是小case啦,如果想看完整的多位数的代码,可以参看  栈的应用2——超级计算器(中缀与后缀表达式)C语言

  • 相关阅读:
    Openstack Swift 原理、架构与 API 介绍
    ReentrantLock 以及 AQS 实现原理
    AtomicInteger源码分析——基于CAS的乐观锁实
    深入浅出ThreadLocal
    Spring IOC的理解
    tomcat8 注册成服务后接sql数据失败
    Video.js 截图 Failed to execute 'drawImage' on 'CanvasRenderingContext2D'
    H5 播放Hls
    Video.js 源码浅析
    Hls流播放延时
  • 原文地址:https://www.cnblogs.com/itlqs/p/4749998.html
Copyright © 2020-2023  润新知