使用栈进行表达式求解

背景知识

 表达式的三种标识方法:

设  exp = S1 OP S2

则称    OP  S1  S2      为前缀表示法

S1  OP  S2      为中缀表示法

S1 S2 OP        为后缀表示法

例如:    exp = a * b + (c - d / e) * f

前缀式:           +   *  a b * - c / d e f

中缀式:           a * b + c - d / e * f

后缀式:           a b * c d e / - f *

结论:1)操作数之间的相对次序不变;

2)运算符的相对次序不同;

3)中缀式丢失了括弧信息, 致使运算的次序不确定;

4)前缀式的运算规则为: 连续出现的两个操作数和在它们 之前且紧靠它们的运算符构成一 个最小表达式;

5)后缀式的运算规则为:  运算符在式中出现的顺序恰为表达式的运算顺序;  每个运算符和在它之前出现 且紧靠它的两个操作数构成一个最小表达式。

 

 

第一种实现方式【一个栈】

 例题:对于一个基于二元运算符的算术表达式,转换为对应的后缀式,并输出之。输入一个算术表达式,以‘#’字符作为结束标志。输出该表达式转换所得到的后缀式。

 

  解题思想:利用栈来解决该问题,大体分为几种情况:

                     ①输入指令为数时,无需入栈,直接输出。

                     ②输入指令为加减时,比较栈顶元素,若栈顶为加减乘除,栈顶出栈,一直出栈出到没有元素或遇到前括号为止,随后该元素进栈成为栈顶元素。

                     ③输入指令为乘除时,比较栈顶元素,若栈顶为加减,栈顶元素的优先级比较低,这样的话无需出栈,该元素直接进栈;若栈顶元素为同级的乘除时,栈顶元素出栈,继续检验下一个新的栈顶元素,直到遇到前括号或者栈内没有元素为止,随后该元素进栈。

                     ④输入指令为前括号时:直接进栈。

                     ⑤输入指令为后括号时:与前括号之间的所有元素,按照后入先出的顺序依次出栈。

 

可以这样理解:优先级  乘除>加减>前括号

输入指令是数字的时候,直接发给后缀式,

输入指令是加减乘除的时候,比较栈顶元素,如果输入指令优先级比栈顶元素高,就入栈,否则栈顶元素就出栈发给后缀式,直到输入指令优先级高于栈顶元素优先级或栈空,再入栈

输入前括号的时候,直接进栈

输入后括号的时候,与前括号之间的所有元素,按照后入先出的顺序依次出栈,但是括号不写入后缀式

#include <iostream>
#include <stdio.h>
#include <stdlib.h>
#define maxsize 10000
typedef char elemtype;

typedef struct
{
    char *base;
    char *top;
} sq;

int init(sq &s)                                                
{
    s.base=(elemtype *)malloc(maxsize *sizeof(elemtype));
    s.top=s.base;
    return 0;
}                                                                     

int main()
{
    sq s;
    init(s);
    char a[1000];
    gets(a);
    int i;
    for(i=0; a[i]!='#'; i++)
    {
        if(a[i]=='(')
            *++s.top=a[i];
        else if(a[i]>='a'&&a[i]<='z')
            printf("%c", a[i]);
        else if(a[i]==')')
        {
            while(*s.top!='('&&s.top!=s.base)
                printf("%c", *s.top--);
            s.top--;
        }
        else if(a[i]=='+'||a[i]=='-')
        {
            while(*s.top!='('&&s.top!=s.base)
            {
                printf("%c", *s.top--);
            }
            *++s.top=a[i];
        }
        else if(a[i]=='*'||a[i]=='/')
        {
            while(s.top!=s.base&&*s.top!='+'&&*s.top!='-'&&*s.top!='(')
            {
                printf("%c", *s.top);
                s.top--;
            }
            *++s.top=a[i];
        }
    }
    while(s.base!=s.top)
    {
        printf("%c", *s.top);
        s.top--;
    }
    printf("\n");
    return 0;
}

第二种实现方式【两个栈】

 

为了实现用栈计算算数表达式的值,需设置两个工作栈:用于存储运算符的栈opter,以及用于存储操作数及中间结果的栈opval。

算法基本思想如下:

(1)首先将操作数栈opval设为空栈,而将'#'作为运算符栈opter的栈底元素,这样的目的是判断表达式是否求值完毕。

(2)依次读入表达式的每个字符,表达式须以'#'结尾,若是操作数则入栈opval,若是运算符,则将此运算符c与opter的栈顶元素top比较优先级后执行相应的操作,(具体操作如下:(i)若top的优先级小于c,即top<c,则将c直接入栈opter,并读入下一字符赋值给c;(ii)若top的优先级等于c,即top=c,则弹出opter的栈顶元素,并读入下一字符赋值给c,这一步目的是进行括号操作;(iii)若top优先级高于c,即top>c,则表明可以计算,此时弹出opval的栈顶两个元素,并且弹出opter栈顶的的运算符,计算后将结果放入占opval中。)直至opter的栈顶元素和当前读入的字符均为'#',此时求值结束。

算符间的优先关系如下表所示:

表中需要注意的是θ1为opter的栈顶元素,θ2位从表达式中读取的操作符,此优先级表可以用二维数组实现,具体见代码(表来源:严蔚敏《数据结构》)。

#include<iostream>     //输入的表达式要以'#'结尾,如‘5+6*3/(3-1)#’
#include<cstring>
#include<cstdio>
#include<cctype>
#include<stack>
using namespace std;
 
stack<char> opter;    //运算符栈
stack<double> opval;  //操作数栈
 
int getIndex(char theta)   //获取theta所对应的索引
{
	int index = 0;
	switch (theta)
	{
	case '+':
		index = 0;
		break;
	case '-':
		index = 1;
		break;
	case '*':
		index = 2;
		break;
	case '/':
		index = 3;
		break;
	case '(':
		index = 4;
		break;
	case ')':
		index = 5;
		break;
	case '#':
		index = 6;
	default:break;
	}
	return index;
}
 
char getPriority(char theta1, char theta2)   //获取theta1与theta2之间的优先级
{
	const char priority[][7] =     //算符间的优先级关系
	{
		{ '>','>','<','<','<','>','>' },
		{ '>','>','<','<','<','>','>' },
		{ '>','>','>','>','<','>','>' },
		{ '>','>','>','>','<','>','>' },
		{ '<','<','<','<','<','=','0' },
		{ '>','>','>','>','0','>','>' },
		{ '<','<','<','<','<','0','=' },
	};
 
	int index1 = getIndex(theta1);
	int index2 = getIndex(theta2);
	return priority[index1][index2];
}
 
double calculate(double b, char theta, double a)   //计算b theta a
{
	switch (theta)
	{
	case '+':
		return b + a;
	case '-':
		return b - a;
	case '*':
		return b * a;
	case '/':
		return b / a;
	default:
		break;
	}
}
 
double getAnswer()   //表达式求值
{
	opter.push('#');      //首先将'#'入栈opter
	int counter = 0;      //添加变量counter表示有多少个数字相继入栈,实现多位数的四则运算
	char c = getchar();
	while (c != '#' || opter.top() != '#')   //终止条件
	{
		if (isdigit(c))   //如果c在'0'~'9'之间
		{
			if (counter == 1)   //counter==1表示上一字符也是数字,所以要合并,比如12*12,要算12,而不是单独的1和2
			{
				double t = opval.top();
				opval.pop();
				opval.push(t * 10 + (c - '0'));
				counter = 1;
			}
			else
			{
				opval.push(c - '0');     //将c对应的数值入栈opval
				counter++;
			}
			c = getchar();
		}
		else
		{
			counter = 0;   //counter置零
			switch (getPriority(opter.top(), c))   //获取运算符栈opter栈顶元素与c之间的优先级,用'>','<','='表示
			{
			case '<':               //<则将c入栈opter
				opter.push(c);
				c = getchar();
				break;
			case '=':               //=将opter栈顶元素弹出,用于括号的处理
				opter.pop();
				c = getchar();
				break;
			case '>':               //>则计算
				char theta = opter.top();
				opter.pop();
				double a = opval.top();
				opval.pop();
				double b = opval.top();
				opval.pop();
				opval.push(calculate(b, theta, a));
			}
		}
	}
	return opval.top();   //返回opval栈顶元素的值
}
 
int main()
{
	//freopen("test.txt", "r", stdin);
	int t;     // 需要计算的表达式的个数
	cin >> t;
	getchar();
	while (t--)
	{
		while (!opter.empty())opter.pop();
		while (!opval.empty())opval.pop();
		double ans = getAnswer();
		cout << ans << endl<< endl;
		getchar();
	}
	return 0;
}

 

  • 3
    点赞
  • 26
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值