数据结构-栈-表达式求值

表达式

  • 计算规则
    从左到右,先乘除,后加减。有括号先算括号

  • 构成元素
    加(+)、减(-)、乘(*)、除(/)、 括号( () ),也可含空格( )

  • 计算过程
    先将中缀表达式转换成后缀表达式,再对后缀表达式进行具体求值。
    在具体的代码实现中,两个步骤混在一起
    需借助两个栈来实现计算。一个操作数栈,用来存储表达式中的操作数;另一个是运算符栈,用来存储表达式中的运算符

    常见的表达式为中缀表达式,如 9 +(3-1)* 3 + 10 / 2
    后缀表达式不含括号,如 9 3 1 - 3 * + 10 2 / +

  • 优先级设定
    优先级针对表达式中的运算符而言,分为栈内优先级、栈外优先级两种。

    无论栈内栈外,乘除的优先级始终高于加减的优先级,乘除的优先级相同,加减的优先级相同
    相同的运算符,栈内的优先级高于栈外的优先级
    栈外,‘(’ 的优先级高于所有的运算符;栈内,则低于所有的运算符
    (具体代码时,’ ( ’ 直接入栈)
    栈外,‘)’ 的优先级低于所有的运算符;栈内,则高于所有的运算符。
    (具体代码时,’ ) ’ 不会入栈)
    在这里插入图片描述

    之所以这样分栈内、栈外优先级,个人认为是缘于计算规则中的 “从左到右” 规定。
    假设表达式中仅有两个相同优先级的运算符,如 1 + 2 -3。
    设左侧的 + 运算符为 lope,右侧的 - 运算符为 rope。按照 从左到右 的计算规则,lope 应比 rope 先参与运算。
    在具体代码实现中,需从左至右依次扫描表达式中的每个字符,lope 先入栈。当扫描到 rope 时(此时 rope 尚未入栈,而 lope 已经入栈),为了满足 从左到右 的计算规则(即实现先计算 lope 的目的),需赋予 lope(栈内) 比 rope(栈外) 更高的运算优先级。
    简而言之,加减乘除运算符入栈后优先级升高

在这里插入图片描述
代码主要阅读顺序:main - fun,在 fun 中按需阅读额外的函数代码

#include <iostream>
#include <string>
#include <stack>
using namespace std;

bool isOpe(char ch) // 判是否为加减乘除运算符 
{
    switch (ch)
    {
    case '+':
    case '-':
    case '*':
    case '/':
        return true;
    default:
        return false;
    }
}
bool isNum(char ch)
{
    if ((ch - '0') >= 0 && (ch - '0') <= 9)
        return true;
    return false;
}
int cmp(char inStack, char outOfStack)
{
    /* 
		栈内运算符 inStack   栈外运算符  outOfStack
		前者优先级大,返回正数 1
		后者优先级大,返回负数 -1
		运算符栈内元素只可能为 # + — * / ( 
	*/ 
    switch (inStack)
    {
    case '+':
        if (outOfStack == '*' || outOfStack == '/')
        {
            return -1;
        }
        else
        {
            return 1;
        }
    case '-':
        if (outOfStack == '*' || outOfStack == '/')
        {
            return -1;
        }
        else
        {
            return 1;
        }
    case '*':
        return 1;
    case '/':
        return 1;
    case '(':
        if (outOfStack == '+' || outOfStack == '-' || outOfStack == '*' || outOfStack == '/')
        {
            return -1;
        }
    case '#':
        if (outOfStack == '+' || outOfStack == '-' || outOfStack == '*' || outOfStack == '/')
            return -1;
    }
}
double cal(char ch, double num1, double num2)
{
    switch (ch)
    {
    case '+':
        return num1 + num2;
    case '-':
        return num1 - num2;
    case '*':
        return num1 * num2;
    case '/':
        if (num2 == 0)
        {
            cout << "除数为0" << endl;
            exit(-2);
        }
        else
        {
            return num1 / num2;
        }
    }
}

double fun(string str)
{
    /*
		操作符栈 OpeStack 设为 char 型  
		操作数栈 NumStack 设为 double 型(考虑到除法运算)
		引入 STL 中的 stack 避免编写两种不同的栈相关代码(引入模板也可达到相同目的)	 
	*/
	stack<char> OpeStack;
    stack<double> NumStack;
    
    /*
	  初始时,OpeStack 为空,无法进行后续的 操作符优先级比较 运算,
	  故先入栈一个额外的字符 '#' 来满足后续计算,其优先级比栈内 '(' 低 
	*/ 
    OpeStack.push('#'); 
    
    /*
		表达式中的操作数位于 运算符和空格之间,
		操作数可能仅为 1 位,如 3,也可能多位,如 333 
		引入 num_flag 来标记操作数的第一位数 
		进而达到 让 多位的操作数 参与运算 的目的
	*/ 
    bool num_flag = false;
    
    // 从左到右,依次扫描表达式中的每个字符 
    for (int i = 0; i < str.length(); i++)
    {
        if (str[i] == ' ') // 若字符为空格,则跳过 
        {
            continue;
        }
        else if (isNum(str[i])) // 若字符为数字,则接下来分析是否为多位的操作数 
        {
            /*
				以 12+ 为例
				扫描至 1 时,num_flag为 false ,表示其为数字 12 的第一位数字,
				    直接入栈 NumStack 即可,并修改 num_flag 为 true 
				扫描至 2 时,num_flag 为true,表示 2 不是 数字 12 的第一位数字
				    需从  NumStack 中取出 2 前面已入栈的数字,此为数字 1。 
				    故将 NumStack 栈顶 的 1 出栈,与 2 组成数字 12 后
				    再将完整的多位数数字 12 入栈  NumStack
				扫描至 + 时,非数字,num_flag 修改为 false,
					表示其前的多位数数字扫描完毕 
			*/ 
			
			if (num_flag) // 若为多位的操作数,则将相应字符进行转化为数字 
            {
               /* 
			     STL-stack 中, pop() 的返回类型为 void 
			     为达到使用 出栈元素 的目的,先取栈顶元素,后出栈该元素 
			   */ 
			    double temp = NumStack.top();
                NumStack.pop();
                temp = temp * 10 + str[i] - '0';
                NumStack.push(temp);
                num_flag = true;
            }
            else   
            {
            	// 标记操作数的第一位数,直接入栈该数
                NumStack.push(str[i] - '0');
                num_flag = true;     
            }
        }
        else if (isOpe(str[i]))  // 若扫描到加减乘除运算符,则与 OpeStack 的栈顶元素进行优先级比较 
        {
            num_flag = false;   // 表示其前的多位数数字扫描完毕 
            if (cmp(OpeStack.top(), str[i]) < 0) // 若栈外运算符的优先级大 
            {
                OpeStack.push(str[i]);
            }
            else
            {
            	/*
					若栈外运算符的优先级小
				*/
                while (cmp(OpeStack.top(), str[i]) > 0)
                {
                    double num1 = NumStack.top(); 
                    NumStack.pop();
                    double num2 = NumStack.top();
                    NumStack.pop();
                    char ch = OpeStack.top();
                    OpeStack.pop();
                    
                    /*
					 	表达式越左的操作数,越先入栈 NumStack 
						实际运算顺序 num2 ch num1 
					*/ 
                    double num3 = cal(ch, num2, num1); 
                    NumStack.push(num3);
                }
                OpeStack.push(str[i]);
            }
        }
        else if (str[i] == '(')   // '(' 直接入栈 
        {
            num_flag = false;
            OpeStack.push(str[i]);
        }
        else if (str[i] == ')')  // 扫描至 ')'时,依次出栈 OpeStack 元素参与运算,直至 '(' 
        {
            while (OpeStack.top() != '(')
            {
                double num1 = NumStack.top();
                NumStack.pop();
                double num2 = NumStack.top();
                NumStack.pop();
                char ch = OpeStack.top();
                OpeStack.pop();
                double num3 = cal(ch, num2, num1);
                NumStack.push(num3);   // 运算结果入栈 
            }
            OpeStack.pop();
        }
    }
    while (OpeStack.top() != '#')  // 将 OpeStack 中的剩余运算符出栈参与运算 
    {
        double num1 = NumStack.top();
        NumStack.pop();
        double num2 = NumStack.top();
        NumStack.pop();
        char ch = OpeStack.top();
        OpeStack.pop();
        double num3 = cal(ch, num2, num1);
        NumStack.push(num3);
    }
    return NumStack.top();
}
int main()
{
    string str;// 使用 string 字符串来获取表达式 
    cout << "输入一个表达式" << endl;
    cin >> str;
    cout << "运算结果为" << endl;
    cout << fun(str) << endl;
    return 0;
}
  • 3
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值