引入

在20世纪30年代,波兰逻辑学家扬·卢卡西维茨(Jan Łukasiewicz),发明了一种将所有操作符置于操作数的后面,因此也被称为后缀表示法。我们称之为逆波兰记法。逆波兰记法不需要括号来标识操作符的优先级。

逆波兰表示法

上面的引入感觉很高大上,我们来个例题,对于解

$(1-2)\times (4+5)$

连小朋友都知道,先算括号里面的,再算括号外面的,最后结果相乘。
如果计算机来解题的话,就会用很多 if…else… 进行判断,显然对于我们人类方便的括号优先级表达法,对计算机不那么友好。
那么能不能对计算机友好一点?逆波兰表达式出现了,对于上面的问题:

普通式为:$(1-2)\times (4+5)$
逆波兰表达式为:$12-45\times$

基本的转换方法如下

  • 所有数字从左到右写,位置不用动
  • 遇到括号,先写出括号内的数字
  • 然后在数字后面,以先乘除后加减的方式写出括号内的运算符
  • 然后继续写数字,直到所有数字写完
  • 然后在数字后面,以先乘除后加减的方式写出剩余的运算符

几个例子
$a+b$ 转换为:$ab+$
$a+(b-c)$ 转换为:$abc-+$
$a+(b-c) \times d$ 转换为:$abc-d \times+$
$a+b \times (b-c)$ 转换为:$adbc- \times +$

主判断逻辑

栈的创建、初始化、push、pop、length等操作我就不说了,主要讲这个逆波兰的判断。对了,栈内只存符号。
首先判断数字
如果输入的是数字

  • 如果是0~9之间的数字,就直接输出
  • 然后获取下一个输入的值
  • 如果还是数字,就直接输出
  • 如果不是数字,那就输出空格。
  • 这样就完成了判断数字位数的问题。

判断括号

  • 如果输入了反括号,那就证明括号内的内容结束了
  • 我们要把括号内的符号提取出来
  • 没有提取到正括号,那就继续提取里面的符号

判断加减
如果栈内没有符号

  • 输入的如果是加号或者减号
  • 如果栈是空栈,那就压入符号,然后继续接收输入

如果栈内本身有符号

  • 那就先把符号取出来
  • 如果是正括号,那没事,证明要压入的符号是括号内的运算符
  • 直接压入符号即可
  • 如果不是正括号,那就直接把里面的符号输出,因为有新的符号了
  • 判断如果栈不为空并且也不是正括号的话,那就证明栈里面还有运算符,并且这个运算符不是括号内的运算符

判断乘除

  • 如果符号不是+ -,是* / ( 的话
  • 那就直接压栈

判断结束

  • 如果输入的是 # 号的话
  • 那就直接 break 跳出循环

啥都不是

  • 如果输入的是非法字符,则输出错误

继续循环

  • 如果输入的是符号且不是非法字符。
  • 那就继续接收新的值,继续执行循环。

代码实现

#include <stdio.h>
#include <stdlib.h>

#define STACK_INIT_SIZE 20
#define STACKINCREMENT  10

typedef char ElemType;
typedef struct
{
    ElemType *base;
    ElemType *top;
    int stackSize;
}sqStack;

void InitStack(sqStack *s)
{
    s->base = (ElemType *)malloc(STACK_INIT_SIZE * sizeof(ElemType));
    if( !s->base )
        exit(0);
    
    s->top = s->base;
    s->stackSize = STACK_INIT_SIZE;
}

void Push(sqStack *s, ElemType e)
{
    if( s->top - s->base >= s->stackSize )
    {
        s->base = (ElemType *)realloc(s->base, (s->stackSize + STACKINCREMENT) * sizeof(ElemType));
        if( !s->base )
            exit(0);
        s->top = s->base + s->stackSize;
        s->stackSize = s->stackSize + STACKINCREMENT;
    }
    
    *(s->top) = e;
    s->top++;
}

void Pop(sqStack *s, ElemType *e){
    if( s->top == s->base ){
        return;
}
    *e = *--(s->top);
}

int StackLen(sqStack s){
    return (s.top - s.base);
}

int main()
{
    sqStack s;
    char c, e;
    
    InitStack( &s );
    
    printf("请输入逆波兰中缀表达式,以#作为结束标志:");
    scanf("%c", &c);
    
    //开始判断输入的值,如果不遇到#,就继续接收
    while( c != '#' )
    {
        //判断位数,例如判断1或者11,如果输入的是0~9,则直接打印,然后接一个scanf,判断下一个输入的是数字还是符号,数字就继续输出,符号就输出空格
        while( c>='0' && c<='9' )
        {
            printf("%c", c);
            scanf("%c", &c);
            if( c<'0' || c>'9' )
            {
                printf(" ");
            }
        }
        
        //如果输入的内容是反括号,那就取出栈内的符号,然后直到取出正括号为止,就不输出了,就是输出括号中包含的符号
        if( ')' == c )
        {
            Pop(&s, &e);  //把
            while( '(' != e )
            {
                printf("%c", e);
                Pop(&s, &e);
            }
        }
        //如果输入的是+或者之一,则执行下面的内容
        else if( '+'==c || '-'==c )
        {
            //如果是空栈,则直接push,记录这个符号
            if( !StackLen(s) )
            {
                Push(&s, c);
            }
            //如果栈内已经有符号了,先把符号拿出来
            else
            {
                do
                {
                    Pop(&s, &e);
                    if( '(' == e )  //如果取出来的是括号,那就继续入栈,因为入的是括号内的符号
                    {
                        Push(&s, e);
                    }
                    else  //如果取出来的不是括号,那就先把这个符号输出,因为遇到新符号了
                    {
                        printf("%c ", e);
                    }
                }while( StackLen(s) && '('!=e );  //然后判断栈是不是还有东西,并且e不是正括号
                Push(&s, c);  //然后再把符号压栈
            }
        }
        else if( '*'==c || '/'==c || '('==c )  //如果输入的是*、/或者括号,那就直接压栈,因为反括号的情况判断过了
        {
            Push(&s, c);
        }
        else if( '#'== c )  //如果输入的是#号,那就跳出循环
        {
            break;
        }
        else  //如果输入的不是数字、也不是括号或者四则运算符,那就跳出错误
        {
            printf("\n格式错误!\n");
            return -1;
        }
        
        scanf("%c", &c); //如果不是#号,那就继续输入
    }
    
    //将栈中的所有内容输出
    while( StackLen(s) )
    {
        Pop(&s, &e);
        printf("%c ", e);
    }
    
    return 0;
}

尾巴

这是我的个人学习笔记,主要是应付考研复习使用,充斥着一些吐槽和个人观点,并不严谨,欢迎大家参考、指正。