数据结构之四则运算表达式求值(Python和VC中缀表达式转后缀表达式实例代码)

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档


前言

提示:这里可以添加本文要记录的大概内容:

我们再来重点讲 个比较常见的应用:数学达式的求值。20世纪50年代,波兰逻辑学家Jan Lukasiewicz,当时也和我们现在的同学们一样,困惑于如何才可以搞定这个四则运算,不知道他是否也像牛顿被苹果砸到头而想到万有引力的原理,或者还是阿基米德在浴缸中洗澡时想到判断皇冠是否纯金的办法,总之他也是灵感突现,想到了一种不需要括号的后缀表达法,我们也把它称为**逆波兰(Reverse Polish Notation,RPN)**表示。我想可能是他的名字太复杂了,所以后人只用他的国籍而不是姓名来命名,实在可惜。这也告诉我们,想要流芳百世,名字还要起得朗朗上口才行。

码字不易,有问题可以一起学习讨论,一起进步,动动小手三连哦!!!

提示:以下是本篇文章正文内容,下面案例可供参考

一、后缀表示法

这种后缀表示法,是表达式的一种新的显示方式,非常巧妙地解决了程序实现四则运算的难题。
我们先来看看,对于“9+(3-1)×3+10÷2”,如果要用后缀表示法应该是什么样子:“9 3 1-3*+10 2/ +”,这样的表达式称为后缀表达式,叫后缀的原因在于所有的符号都是在要运算数字的后面出现。显然,这里没有了括号。对弈从来没有接触过后缀表达式的同学来讲,这样的表述是很难受的。不过你不喜欢,有机器喜欢,比如比我们聪明的计算机。

二、实现过程解析

后缀表达式:9 3 1 - 3 * + 10 2 / +
规则:从左到右遍历表达式的每个数字和符号,遇到是数字就进栈,遇到是符号,就将处于栈顶两个数字出栈,进行运算,运算结果出栈,一直到最终获得结果。
1.初始化一个空栈,用于对要运算的数字进出使用。
2.后缀表达式中前三个都是数字,所以9、3、1进栈。
在这里插入图片描述
3.接下来是“一”,所以将栈中的1出栈作为减数,3出栈作为被减数,并运算3-1得到2,再将2进栈。接着是数字3进栈,如图所示。
在这里插入图片描述
4.后面是“*”,也就意味着栈中3和2出栈,2与3相乘,得到6,并将6进栈。下面是“+”,所以栈中6和9出栈,9与6相加,得到15,将15进栈,如图所示。
在这里插入图片描述
5.接着是10与2两数字进栈。接下来是符号“/”,因此,栈顶的2与10出栈,10与2相除,得到5,将5进栈,如图所示。
在这里插入图片描述
6.最后一个是符号“+”,所以15与5出栈并相加,得到20,将20进栈。结果是20出栈,栈变为空,如图所示。
在这里插入图片描述
果然,后缀表达法可以很顺利解决计算的问题。现在除了还没睡醒的小伙伴,应该都有同样的疑问,就是这个后缀表达式“9 3 1 - 3 * + 10 2 / +”是怎么出来的?这个问题不搞清楚,等于没有解决。所以下面,我们就来推导如何让“9+(3-1)×3+10÷2”转化为“9 3 1 - 3 * + 10 2 / +”。

三、中缀表达式转后缀表达式

中缀表达式“9 + (3 - 1) × 3 + 10 ÷ 2”转化为后缀表达式“9 3 1 - 3 * + 10 2 / +”。
规则:从左到右遍历中缀表达式的每个数字和符号,若是数字就输出,即成为后缀表达式的一部分;若是符号,则判断其与栈顶符号的优先级,是右括号或优先级低于栈顶符号(乘除优先加减)则栈顶元素依次出栈并输出,并将当前符号进栈,一直到最终输出后缀表达式为止。
1.初始化一空栈,用于对符号进出栈使用。第一个字符是数字9 ,输出9 ,后面是符号"+",进栈。
在这里插入图片描述
2.第三个字符是“(”,依然是符号,因其只是左括号,还未配对,故进栈。第四个字符是数字3,输出,总表达式为9 3,接着是“一”,进栈。
在这里插入图片描述
3.接下来是数字1,输出,总表达式为9 3 1,后面是符号“)”,此时,我们需要去匹配此前的“(”,所以栈顶依次出栈,并输出,直到“(”出栈为止。此时左括号上方只有“-”,因此输出“-”。总的输出表达式为9 3 1 -。接着是数字3,输出,总的表达式为9 3 1 - 3。紧接着是符号“×”,因为此时的栈顶符号为“+”号,优先级低于“×”,因此不输出,“”进栈。
在这里插入图片描述
4. 之后是符号“+”,此时当前栈顶元素“
”比这个“+”的优先级高,因此栈中元素出栈并输出(没有比“+”号更低的优先级,所以全部出栈),总输出表达式为931-3*+。然后将当前这个符号“+”进栈。也就是说,前6张图的栈底的“+”是指中缀表达式中开头的9后面那个“+”,而下图中的栈底(也是栈顶)的“+”是指“9+(3-1)×3+”中的最后一个“+”。紧接着数字10,输出,总表达式变为931-3*+10。后是符号“÷”,所以“/”进栈。
在这里插入图片描述
5.最后一个数字2,输出,总的表达式为9 3 1-3*+10 2。如图4-9-10的左图所示。.因已经到最后,所以将栈中符号全部出栈并输出。最终输出的后缀表达式结果为9 3 1 - 3 * + 10 2 / +。
在这里插入图片描述

四、Python和VC中缀表达式转后缀表达式实例代码

#python代码实例
def middle2behind(expresssion): #中缀表达式转后缀表达式 
    #只支持整数
    result = []             # 结果列表
    stack = []              # 栈
    i = 0                   #表达式索引
    expresssion = list(expresssion)
    expresssion.append('$') #添加结尾标识符
    while(1): 
        if expresssion[0] == '$':
            break
        elif '.'.join(expresssion[:-1]).isdigit():
            result.apppend(int('.'.join(expresssion[:-1])))
            break
            
        item = expresssion[i]
        step = 1
        while (item.isdigit()) and \
            (expresssion[i+step] in "0123456789"):
            item += expresssion[i+step]
            step +=1
#         print(char, stack)
#         if len(stack)!=0:
#             print(stack[-1], stack[-1] in '+-*/((')

        if (item.isdigit()) and \
            ((len(stack)==0) or (stack[-1] in '+-*/((')):
#             print(11111)
            i+=step
            result.append(item)
#             stack.pop()
#             stack.append(item)
#             break
        elif (item in "((") and \
              ((len(stack)==0) or (stack[-1] in '+-*/((')):
#             print(22222)
            i+=step
#             result.append(item)
#             stack.pop()
            stack.append(item)
#             break
        elif (item in "+-") and \
              ((len(stack)!=0) and (stack[-1] in '+-')):
#             print(33333)
            i+=step
            result.append(stack.pop())
            stack.append(item)
#             break
        elif (item in "*/") and \
              ((len(stack)!=0) and (stack[-1] in '+-')):
#             print(44444)
            i+=step
#             result.append(item)
#             stack.pop()
            stack.append(item)
#             break
        elif (item in "+-*/") and \
              ((len(stack)!=0) and (stack[-1] in '*/')):
#             print(55555)
            i+=step
            result.append(stack.pop())
            stack.append(item)
#             break
        elif (item in "+-*/") and \
              ((len(stack)==0) or (stack[-1] in '((')):
#             print(66666)
            i+=step
#             result.append(stack.pop())
            stack.append(item)
#             break
        elif (item in "))") and \
              ((len(stack)!=0) and (stack[-1] in '((')):
#             print(77777)
            i+=step
#             result.append(item)
            stack.pop()
#             stack.append(item)
#             break
        elif (item in "))$") and \
              ((len(stack)!=0) and (stack[-1] in '+-*/')): 
#             print(88888)
#             i+=step
            result.append(stack.pop())
#             stack.append(item)
#             break
        elif (item in "$") and \
              (len(stack)==0): 
#             print(99999)
#             i+=1step
#             result.append(stack.pop())
#             stack.append(item)
            break
        else:
#             print('******')
            result.append('*****') #表达式不符合要求,解析异常
            break
            

    # 返回字符串
    return result


def calculation(postfixExpr): #计算后缀表达式
    stack = []
    if len(postfixExpr) == 1:
        return int(postfixExpr[0])
    for item in postfixExpr:
        if item.isdigit():
            stack.append(int(item))
        else:
            operand2 =stack.pop()
            operand1 =stack.pop()
            result =doMath(item,operand1,operand2)
            stack.append(result)
    return stack.pop()
 
def doMath(op, op1, op2):
    if op == "*":
        return op1 * op2
    elif op == "/":
        return op1 / op2
    elif op == "+":
        return op1 + op2
    else:
        return op1 - op2

#####使用代码实例##############使用代码实例###################使用代码实例##################
expression = "((((1000+2000))-2000)*(40+60))/10000"
postfixExpr = middle2behind(expression)
postfixExpr
if '*****' not in postfixExpr:
    print(calculation(middle2behind(expression)))
else:
    print('表达式解析错误')

### 输出结果:10
// vc 代码实例
#include <iostream>
#include <cstring>
#include <stack>
 
using namespace std;
 
int priority(char op) // 优先级确认
{
    int priority;
    if(op == '*' || op == '/') priority = 2;
    if(op == '+' || op == '-') priority = 1;
    if(op == '(') priority = 0;
    return priority;
}
 
// 转换函数,引用符号提高转换效率
void Trans(string &str, string &str1)
{
    stack<char> s;
    int i;
    for(i = 0; i < str.size(); i ++ )
    {
        //是数字的情况下直接输出
        if(str[i] >= '0' && str[i] <= '9' || str[i] >= 'a' && str[i] <= 'z')
        {
            str1 += str[i];
        }
        else //不是数字的情况分类讨论进行判断
        {
            //栈为空时直接入栈
            if(s.empty()) s.push(str[i]);
            //左括号入栈
            else if(str[i] == '(') s.push(str[i]);
            //如果是右括号,只要栈顶不是左括号,就弹出并输出
            else if(str[i] == ')')
            {
                while(s.top() != '(')
                {
                    str1 += s.top();
                    s.pop();
                }
                //弹出左括号,但不输出
                s.pop();
            }
            else 
            {
                //栈顶元素的优先级大于等于当前的运算符,就将其输出
                while(priority(str[i]) <= priorty(s.top()))
                {
                    str1 += s.top();
                    s.pop();
                    //栈为空,停止
                    if(s.empty()) break;
                }
                s.push(str[i]);
            }
        }
    }
    //最后,如果不为空,就把所以的元素全部弹出
    while(!s.empty())
    {
        str1 += s.top(); 
        s.pop();
    }
}
 
int main()
{
    //输入前缀表达式
    string infix;
    string postfix;
    cin >> infix;
    
    Trans(infix, postfix);
    
    cout << postfix << endl;
    return 0;
}

总结

从刚才的推导中你会发现,要想让计算机具有处理我们通常的标准(中缀)表达式的能力,最重要的就是两步:
1.将中缀表达式转化为后缀表达式(栈用来进出运算的符号)。
2.将后缀表达式进行运算得出结果(栈用来进出运算的数字)。

码字不易,有问题可以一起学习讨论,一起进步,动动小手三连哦!!!

  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
如果你使用 EasyX 的图形库,可以使用 `getch()` 函数来获取键盘输入,然后将输入的字符拼接成表达式字符串,最后使用表达式的算法进行计算。 以下是一个使用 EasyX 实现的简单的表达式程序,可以作为参考: ```c #include <graphics.h> #include <conio.h> #include <string.h> #include <stdlib.h> #include <math.h> #define MAX_EXPR_LEN 100 double eval_expr(const char* expr); double eval_term(const char* expr, int* p); double eval_factor(const char* expr, int* p); int main() { initgraph(640, 480); setbkcolor(WHITE); cleardevice(); setcolor(BLACK); settextstyle(20, 0, "consolas"); char expr[MAX_EXPR_LEN + 1]; int len = 0; expr[0] = '\0'; double result = 0; while (true) { outtextxy(20, 20, "Please enter an expression:"); outtextxy(20, 50, expr); outtextxy(20, 80, "Result:"); char c = getch(); if (c == '\r') // Enter key { result = eval_expr(expr); sprintf(expr, "%.2f", result); len = strlen(expr); } else if (c == 8) // Backspace key { if (len > 0) { expr[--len] = '\0'; } } else if (len < MAX_EXPR_LEN && strchr("0123456789.+-*/()", c)) { expr[len++] = c; expr[len] = '\0'; } cleardevice(); } closegraph(); return 0; } double eval_expr(const char* expr) { int p = 0; return eval_term(expr, &p); } double eval_term(const char* expr, int* p) { double result = eval_factor(expr, p); while (expr[*p] == '*' || expr[*p] == '/') { char op = expr[(*p)++]; double operand = eval_factor(expr, p); if (op == '*') { result *= operand; } else { result /= operand; } } return result; } double eval_factor(const char* expr, int* p) { double result = 0; int sign = 1; if (expr[*p] == '-') { sign = -1; (*p)++; } if (expr[*p] == '(') { (*p)++; result = eval_expr(expr); (*p)++; } else if (isdigit(expr[*p])) { while (isdigit(expr[*p])) { result = result * 10 + (expr[(*p)++] - '0'); } if (expr[*p] == '.') { (*p)++; double fraction = 0.1; while (isdigit(expr[*p])) { result += fraction * (expr[(*p)++] - '0'); fraction *= 0.1; } } } return sign * result; } ``` 这个程序可以接受用户从键盘输入表达式,并计算出结果,然后显示在屏幕上。用户可以使用数字键、运算符键、小数点键、括号键和 Backspace 键来输入表达式

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

逃逸的卡路里

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值