栈的应用(三)之C语言实现逆波兰表达式

今天是清明节放假的第一天,博主也没闲着(拒绝偷懒),早上起来看了数据结构。今天就趁热打铁来讲讲《栈的应用之C语言实现逆波兰表达式》吧。

波兰表达式存在的意义就是使运算式中即使没有括号,也可以执行正确的运算顺序。例如a+b的波兰表达式可以写作+ab,逆波兰表达式则是ab+。

实现逻辑:
1.首先我们拿到一个存放运算式的字符串。创建一个符号栈,用来放-+x/().
2.对字符串逐一扫描。如果是数字我们直接输出。
3.如果是运算符,而且此时符号栈空着的话就直接放进去。如果不是,我们就比较栈顶符号与扫描符号的优先级,±最低,x/中间,()最高。
3.1.扫描符号高于栈顶符号,那就直接入栈,注意是高,等于不行。
3.2扫描符号不高于栈顶符号,输出栈顶符号,然后重复比较栈顶元素。也就是重复第3点。
4.如果扫到(,在定义时我们给了(最高优先级,所以(可以直接入栈,不用特殊操作,但是接下来扫描到的符号优先级肯定比(低,大家想想是不是。但是不能把(由3.2步骤弹出去!(弹出去的唯一条件就是扫描到了)。所以这里就需要在3.2的出栈条件再增加一条:栈顶不是(即可。
5.一直到扫描完这个字符串,然后将符号栈中剩下元素一直出栈输出就行。

这主要是利用栈的先进后出的灵活性。用C语言实现代码(编译环境:vs2019):

#include<stdio.h>
#include<string.h>//使用字符串必备头文件
#include<stdlib.h>//使用malloc必备的头文件
int operatorvalue[6] = { 40,41,42,43,45,47 };   //这是*-+/()的ascll码,用来判别运算式中扫描的字符是否是符号的

typedef struct stack {
    char data;
    struct stack* next;
}stack, * liststack;

void initstack(liststack& s)
{
    
    s = (liststack)malloc(sizeof(liststack));
    s->next = NULL;
    printf("init finished.\n");
}

void push(liststack& s, char c) {            //入栈
    stack* node;
    node = (liststack)malloc(sizeof(stack));
    node->data = c;
    node->next = s->next;
    s->next = node;
}

char pop(liststack& s) {                    //出栈

    stack* top;
    top = s->next;
    char t = top->data;
    s->next = top->next;
    return t;

}

int priority(char c) {                      //return的数字就是运算优先级,这样才能比较
    if (c == '+' || c == '-')
    {
        return 1;
    }
    else if (c == '*' || c == '/')
    {
        return 2;
    }
    else
    {
        return 3;
    }
}

char gettop(liststack s) {                    //获得栈顶元素
    if (s->next != NULL) return s->next->data;
}

int numORoperator(char c) {                    //判断字符是数字还是运算符
    for (int i = 0; i < 6; i++)
    {
        if (operatorvalue[i] == int(c))        //operatorvalue[i]是开篇就定义的运算符ascll码数组
            return 1;        //运算符
    }
    return 0;                //数字
}

void nibolan(liststack& lsop, char arr[]) {        //通过此函数求得逆波兰表达式
    for (int i = 0; i < strlen(arr); i++)        //遍历字符串中所有字符
    {
        if (numORoperator(arr[i]))                //如果是运算符执行if里面语句
        {
            if (lsop->next == NULL)                //如果符号栈空,直接入栈
            {
                push(lsop, arr[i]);
            }
            else if (arr[i] == ')')                //如果符号栈不空并且扫描到了‘)’,我们的逆波兰表达式不需要),所以不要入栈,(入栈都只是因为要它当作一个界限。
            {
                while (gettop(lsop) != '(')        //如果栈顶不是(,那么就一直出栈
                {
                    char c = pop(lsop);
                    printf("%c", c);
                }pop(lsop);                        //注意这里再出栈的目的是让(出栈,我们的逆波兰表达式不需要()。上面循环结束条件就是栈顶为(。
            }
            else                                //如果符号栈不空并且扫描到的不是),就执行这里面的语句
            {
                int toppriority = priority(gettop(lsop));       //获得栈顶元素优先级
                int arripriority = priority(arr[i]);            //获得当前扫描元素的优先级
                if (arripriority > toppriority || gettop(lsop) == '(')        //如果扫描元素优先级高于栈顶优先级,或者栈顶元素是(,则扫描元素入栈
                {
                    push(lsop, arr[i]);
                }
                else   //如果扫描元素优先级不高于栈顶优先级,那就要栈顶出栈了
                {
                    while (arripriority <= toppriority && gettop(lsop) != '(')            //这里是重复判断栈顶与扫描符的优先级
                    {
                        if (lsop->next == NULL) break;
                        char c = pop(lsop);
                        printf("%c", c);
                        toppriority = priority(gettop(lsop));
                    }push(lsop, arr[i]);           //高于或等于扫描符的符号都出栈完了。让扫描符入栈
                }
            }
        }
        else                     //如果是数字,直接输出,这是与最上面的if一起的
        {
            printf("%c", arr[i]);
        }
    }
    while (lsop->next)            //将符号栈中剩余元素出栈
    {
        printf("%c", pop(lsop));
    }
}


int main() {
    liststack lsop;
    initstack(lsop);
    int temp = 0;
    char arr[255];
    printf("请输入中缀表达式:");
    scanf_s("%s", arr,255);//注意这里scanf_s的第三个参数表示输入字符串的最大长度,都是博主                                               踩过的坑
    printf("逆波兰表达式为:\n");
    nibolan(lsop, arr);
    return 0;
}

程序执行结果:

 本贴为博主亲手整理。如有错误,请评论区指出,一起进步。谢谢大家的浏览.

评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值