栈的应用:表达式求值问题

1基本思路
任何一个表达式都是由操作数(operand)、运算符(operator)和界限符(delimiter)组成,其中,操作数可以是常数也可以是被说明为变量或常量的标识符;运算符可以分为算术运算符、关系运算符和逻辑运算符等三类;基本界限符有左右括弧和表达式结束符等 。为了叙述简洁,在此仅限于讨论只含二元运算符的算术表达式。
由于算术运算的规则是:先乘除后加减、先左后右和先括弧内后括弧外,则对表达式进行运算不能按其中运算符出现的先后次序进行。
由于算术运算的规则是:先乘除后加减、先左后右和先括弧内后括弧外,则对表达式进行运算不能按其中运算符出现的先后次序进行。
在计算机中,对这种二元表达式可以有三种不同的标识方法。
假设 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.后缀式的运算规则为:
·运算符在式中出现的顺序恰为表达式的运算顺序;
·每个运算符和在它之前出现且紧靠它的两个操作数构成一个最小表达式;
以下就分“如何按后缀式进行运算”和“如何将原表达式转换成后缀式”两个问题进行讨论。
1如何如何按后缀式进行运算
运算过程为:对后缀式从左向右"扫描",遇见操作数则暂时保存,遇见运算符即可进行运算;此时参加运算的两个操作数应该是在它之前刚刚碰到的两个操作数,并且先出现的是第一操作数,后出现的是第二操作数。由此可见,在运算过程中保存操作数的结构应该是个栈。
2如何将原表达式转换成后缀式
我们先引进一个运算符的“优先数”的概念。给每个运算符赋以一个优先数的值,如下所列:   运算符 # ( + - × /
   优先数 -1 0 1 1 2 2
“#”为结束符。容易看出,优先数反映了算术运算中的优先关系,即优先数“高”的运算符应优先于优先数低的运算符进行运算。
也就是说,对原表达式中出现的每一个运算符是否即刻进行运算取决于在它后面出现的运算符,如果它的优先数"高或等于"后面的运算,则它的运算先进行,否则就得等待在它之后出现的所有优先数高于它的"运算"都完成之后再进行。
因此,从原表达式求得后缀式的规则为:
设立运算符栈;
设表达式的结束符为“#”,预设运算符栈的栈底为“#”;
若当前字符是操作数,则直接发送给后缀式;
若当前字符为运算符且优先数大于栈顶运算符,则进栈,否则退出栈顶运算符发送给后缀式;
若当前字符是结束符,则自栈顶至栈底依次将栈中所有运算符发送给后缀式;
若当前为左括号,输入后缀表达式,若当前为右括号,则将栈中的表达式输入后缀表达式直至遇到左括号
代码如下:
头文件:

#include<stdio.h>
#include<stdlib.h>
#define ElemType char 
#define elemtype float
#define INITSIZE 100//初始时栈的容量
#define ADDEDSIZE 10//每次新增加的容量
typedef struct//char类型结构体,在运算过程中保存操作数
{
    
    
	ElemType* base;
	ElemType* top;
	int stacksize;
}stack;
typedef struct//float类型结构体,用于计算后缀表达式的值
{
    
    
	elemtype * base;
	elemtype* top;
	int stacksize;
}stackint;
void InitStack(stack& S);//初始化
bool GetTop(stack& S, ElemType& e);//返回栈顶元素
void jinzhan(stack& S, ElemType e);//进栈操作
bool chuzhan(stack& S, ElemType* e);//出栈操作
int getlength(stack& S);//总长度
bool isEmpty(stack& S);//判空
bool compareOperator(char i, char u);//比较操作数大小
void polrear(stack& Pol, char PolRear[1000], char& temp1, char& temp2, int &length);//构造后缀表达式
bool Gettop(stackint& S, float& e);//返回栈顶元素
void Jinzhan(stackint& S, float  e);//进栈操作
bool Chuzhan(stackint& S, float* e);//出栈操作
bool Chuzhan(stackint& S, float* e);//出栈操作
void initstack(stackint& S);//初始化

资源文件:

#include"stack.h"
void InitStack(stack& S)//初始化
{
    
    
	S.base = (ElemType*)(malloc(INITSIZE * sizeof(ElemType)));//申请空间
	S.top = S.base;//栈空:top=base 
	S.stacksize = INITSIZE;
}
bool GetTop(stack& S, ElemType& e)//返回栈顶元素
{
    
    
	if (S.top == S.base)//栈为空
		return false;
	e = *(S.top - 1);
	return true;
}
void jinzhan(stack& S, ElemType e)//进栈操作
{
    
    
	if (S.top - S.base >= S.stacksize)//栈满
	{
    
    
		S.base = (ElemType*)(realloc(S.base, (S.stacksize + ADDEDSIZE) * sizeof(ElemType)));//栈满,申请空间,此处使用realloc函数在原有的指针域继续申请空间
		S.top = S.base + S.stacksize;//栈顶赋值
		S.stacksize += ADDEDSIZE;
	}
	*S.top = e;//栈顶元素
	S.top++;
}
bool chuzhan(stack& S, ElemType* e)//出栈操作
{
    
    
	if (S.top == S.base)//栈为空
		return false;
	S.top--;
	*e = *S.top;
	return true;
}
int getlength(stack& S)//总长度
{
    
    
	int i;
	i = S.top - S.base;
	return i;
}
bool isEmpty(stack& S)
{
    
    
	if (S.base == S.top)
		return false;
	return true;
}
bool compareOperator(char i, char u)//比较运算符大小
{
    
    
	if ((i == '/' || i == '*') && (u != '*' && u != '/'))//排除所有i大于u的情况即可
		return true;
	else if ((i == '+' || i == '-') && u != '+' && u != '-' && u != '*' && u != '/')
		return true;
	else if (i == '(' || i == ')' && u == '#')
		return true;
	else
		return false;
}
void	polrear(stack& Pol, char PolRear[1000], char& temp1, char& temp2, int &length)//构造后缀表达式
{
    
    
	jinzhan(Pol, '#');//初始化
	while (1)
	{
    
    
		temp1 = getchar();//读入字符
		if (temp1 == '#')//#为结束字符
			break;
		if ('0' <= temp1 && temp1 <= '9')//操作数为数字,输入后缀表达式
		{
    
    
			*(PolRear+ length) = temp1;
			length++;
		}
		else if (temp1 == '(')//左括号直接进栈
		{
    
    
			jinzhan(Pol, temp1);
		}
		else if (temp1 == ')')//将栈中字符输入后缀表达式,注意所有括号均不输入后缀表达式
		{
    
    
			do {
    
    
				chuzhan(Pol, &temp1);
				if (temp1 == '(')//读到右括号,停止
					break;
				*(PolRear + length) = temp1;
				length++;
			} while (1);
		}
		else if (temp1 != ')' && temp1 != '#'&&temp1!='(')//读入运算符
		{
    
    
			GetTop(Pol, temp2);//获取栈顶元素
			if (compareOperator(temp1, temp2))//比较大小,temp1更大则进栈
				jinzhan(Pol, temp1);
			else
			{
    
    
				while (!compareOperator(temp1, temp2))//将栈中元素输入后缀表达式直至遇到大于temp1的字符
				{
    
    
						*(PolRear + length) = temp2;
						length++;
						chuzhan(Pol, &temp2);
					GetTop(Pol, temp2);
				}
				jinzhan(Pol, temp1);//进栈
			}
		}
	}//结束键盘的读入,将余下的字符输入后缀表达式
	do
	{
    
    
		chuzhan(Pol, &temp2);
		if (temp2 != '(')//括号不入栈
		{
    
    
			*(PolRear + length) = temp2;
			length++;
		}
	} while (temp2 != '#');
}
bool Gettop(stackint& S, elemtype& e)//返回栈顶元素,与上述函数完全相同,只是数据类型为float
{
    
    
	if (S.top == S.base)
		return false;
	e = *(S.top - 1);
	return true;
}
void Jinzhan(stackint& S, elemtype  e)//进栈操作
{
    
    
	if (S.top - S.base >= S.stacksize)//栈满
	{
    
    
		S.base = (elemtype*)(realloc(S.base, (S.stacksize + ADDEDSIZE) * sizeof(elemtype)));//栈满,申请空间,此处使用realloc函数在原有的指针域继续申请空间
		S.top = S.base + S.stacksize;
		S.stacksize += ADDEDSIZE;
	}
	*S.top = e;
	S.top++;
}
bool Chuzhan(stackint& S, elemtype* e)//出栈操作
{
    
    
	if (S.top == S.base)
		return false;
	S.top--;
	*e = *S.top;
	return true;
}
void initstack(stackint& S)//初始化
{
    
    
	S.base = (elemtype*)(malloc(INITSIZE * sizeof(elemtype)));
	S.top = S.base;//栈空:top=base 
	S.stacksize = INITSIZE;
}

main函数:

#include"stack.h"
int main()
{
    
    
	stack Pol;//构造后缀表达式所用的栈,为char类型
	stackint S;//计算后缀表达式的值所用到的栈,为float类型
	ElemType temp1;
	ElemType temp2;
	ElemType temp3;
	ElemType temp4;//临时变量
	float a, b,d;
	float c;
	int length=0;
	char PolRear[1000];//后缀表达式,此处不能使用栈结构表示后缀表达式,仔细理解其构造过程和运算过程可知,这应该是个先入先出结构
	int temp = 0;
	InitStack(Pol);
	polrear(Pol, PolRear,temp1,temp2,length);
	for (int i = 0; i < length; i++)
		printf("%c", PolRear[i]);
	initstack(S);
	do//计算后缀表达式的值
	{
    
    
		if (temp+1!=length)
		{
    
    
			temp1 = PolRear[temp];
			temp++;
		}
		else
			break;
		c = temp1 - 48;//asc码数字与真实数字相差48
		if ('0' <= temp1 && '9' >= temp1)
		{
    
    
			Jinzhan(S, c);
		}
		else//注意运算顺序,先出栈的为被操作数
		{
    
    
			Chuzhan(S,&a);
			Chuzhan(S,&b);
			if (temp1 == '+')
				c = a + b;
			else if (temp1 == '-')
				c = b -a;
			else if (temp1 == '*')
				c = a * b;
			else if (temp1 == '/')
				c = b / (a * 1.0);
			Jinzhan(S, c);
		}
	} while (1);
	printf("%f", c);
	return 0;
}

猜你喜欢

转载自blog.csdn.net/weixin_51235620/article/details/115413455