首页 > 代码库 > 栈的典型应用-表达式求值【转】

栈的典型应用-表达式求值【转】

本文转载自:http://www.voidcn.com/blog/lub0807/article/p-1510616.html

栈的一个应用是求四则运算表达式的值,这里的表达式包含数字、加减乘除4种运算符,以及小括号。

由于输入是一个字符串,因此解决这个问题需要以下3个步骤:

1、输入字符串转化为中缀表达式;

2、中缀表达式转化为后缀表达式;

3、后缀表达式求值。

现在表达式为:9 + ( 3 - 1 )* 3 + 10 / 2 ,先看一下运行结果:

技术分享

首先解释一下中缀表达式和后缀表达式的概念。所谓中缀表达式,就是我们平常书写的表达式,因为运算符是写在两个参与运算的数字中间,所以叫中缀表达式,例如1 + 2 。与此对应,后缀表达式就是运算符写在数字后面,比如刚才的算式就要写成1 2 + ,我们看起来的确有点奇怪,不过计算机却很喜欢这种表达式。下面分析解决问题的3个步骤。

1、输入字符串转化为中缀表达式

运算符号和括号好办,本身就是字符,只需要把字符串形式的数字转成相应的整数。我采用的方法是,用一个数组num[]存储整数的各位数字,用一个整型变量count记录位数。当下一个字符是运算符或者括号时,表示整数已经读取完毕。这时候,把num数组的各位数字乘以10的某次方就可还原出该整数。该部分代码如下:

//字符串转换成中缀表达式void StringToMidExp(const char exp[], Stack *ps){	int num[MAX_INT];	int k, n, count, temp;	Stack m_stack;	Stack *pm;	Node *q;	k = 0;	count = 0;	pm = &m_stack;	InitStack(pm);	while (exp[k] != ‘\0‘)	{		if (exp[k] >= ‘0‘ && exp[k] <= ‘9‘) //数字0到9		{			count++;					//count记录整数的位数			num[count-1] = exp[k] - 48;	//num数组记录整数的每一位		}		else if ((exp[k] >= 40 && exp[k] <= 43) || exp[k] == 45 || exp[k] == 47) //运算符		{			if (count > 0) //转换该运算符之前的数字			{				n = 0;				temp = 0;				while (n < count)				{					temp += num[n] * TenPow(count - n -1); //每一位乘以10的某次方					n++;				}								q = (Node *)malloc(sizeof(Node));				q->type = NUM;				q->number = temp;				Push(pm, q);			}			count = 0; //位数清零			q = (Node *)malloc(sizeof(Node));			q->type = OP;			q->operation = exp[k];			Push(pm, q);		}		k++;	}	if (count > 0) //把最后一个数字转换出来	{		n = 0;		temp = 0;		while (n < count)		{			temp += num[n] * TenPow(count - n -1);			n++;		}		q = (Node *)malloc(sizeof(Node));		q->type = NUM;		q->number = temp;		Push(pm, q);	}	Reverse(pm, ps); //颠倒一下次序}//计算10的n次方int TenPow(int n){	if (n == 0)	{		return 1;	} 	else	{		int i, k;		i = 0;		k = 1;		while (i < n)		{			k *= 10;			i++;		}		return k;	}}

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

 

这里需要一个栈用来暂时存储运算符和括号,具体地,对中缀表达式从左到右按以下规则操作:

(1)遇到数字,则直接输出;

(2)遇到左括号,则左括号进栈;

(3)遇到右括号,从栈顶开始依次输出所有运算符,直到遇到左括号,这个左括号也出栈;

(4)遇到加号或减号,从栈顶开始依次输出所有运算符,直到遇到左括号,但此时这个左括号不出栈,并且当前运算符进栈;

(5)遇到乘号或除号,如果栈顶是乘号或除号,则输出,否则不输出,并且当前运算符进栈。

这部分代码如下:

//中缀表达式转换成后缀表达式void MidExpToBackExp(Stack *pm, Stack *pb){	Stack tempStack, oprStack;	Stack *pt, *pr;	Node *q, *r;	pt = &tempStack;	//临时存储后缀表达式	pr = &oprStack;		//用来决定运算符的顺序	InitStack(pt);	InitStack(pr);	while (pm->top)	{		q = (Node *)malloc(sizeof(Node));		Pop(pm, q);		if (q->type == NUM)		{			Push(pt, q);		} 		else		{			if (q->operation == ‘+‘ || q->operation == ‘-‘)			{				while (pr->top && pr->top->operation != ‘(‘)				{					r = (Node *)malloc(sizeof(Node));					Pop(pr, r);					Push(pt, r);				}				Push(pr, q);			}			else if (q->operation == ‘*‘ || q->operation == ‘/‘)			{				while (pr->top && pr->top->operation != ‘(‘ && pr->top->operation != ‘+‘ && pr->top->operation != ‘-‘)				{					r = (Node *)malloc(sizeof(Node));					Pop(pr, r);					Push(pt, r);				}				Push(pr, q);			}			else if (q->operation == ‘(‘)			{				Push(pr, q);			} 			else			{				while (pr->top)				{					if (pr->top->operation == ‘(‘)					{						r = (Node *)malloc(sizeof(Node));						Pop(pr, r);						free(r);						break;					}					r = (Node *)malloc(sizeof(Node));					Pop(pr, r);					Push(pt, r);				}				free(q);			}		}	}	while (pr->top) //栈内剩余运算符全部出栈	{		r = (Node *)malloc(sizeof(Node));		Pop(pr, r);		Push(pt, r);	}	Reverse(pt, pb); //颠倒一下次序}

3、后缀表达式求值

 

这里还需要一个栈,只不过这回是用来暂时存储数字,注意到后缀表达式中不含有括号了。具体地,对后缀表达式从左到右按以下规则操作:

(1)遇到数字,则数字进栈;

(2)遇到运算符,则栈顶数字出栈,记为num1,此时栈顶数字再出栈,记为num2,那么记num2 运算 num1 = num3,将num3进栈。还是举个例子好了,比如遇到运算符+,栈顶数字是1,好了,1出栈。现在栈顶数字变为2了,好,2也出栈。现在计算2+1=3,此时,3进栈。

这部分代码如下:

//根据后缀表达式计算结果int BackExpToResult(Stack *ps){	if (!ps->top) //空栈说明表达式有误	{		return NO_RESULT;	}	Stack tempStack;	Stack *pt;	Node *q;	int num_left, num_right, result;	pt = &tempStack;	InitStack(pt);	while (ps->top)	{		if (ps->top->type == NUM)		{			q = (Node *)malloc(sizeof(Node));			Pop(ps, q);			Push(pt, q);		} 		else		{			q = (Node *)malloc(sizeof(Node));			Pop(pt, q);			num_right = q->number;			free(q);			if (!pt->top) //pt栈内没有第2个数了,说明表达式有误			{				return NO_RESULT;			}			q = (Node *)malloc(sizeof(Node));			Pop(pt, q);			num_left = q->number;			free(q);			q = (Node *)malloc(sizeof(Node));			Pop(ps, q);			switch(q->operation)			{			case ‘+‘:				result = num_left + num_right;				break;			case ‘-‘:				result = num_left - num_right;				break;			case ‘*‘:				result = num_left * num_right;				break;			case ‘/‘:				result = num_left / num_right;				break;							}			free(q);			q = (Node *)malloc(sizeof(Node));			q->type = NUM;			q->number = result;			Push(pt, q);		}	}	q = (Node *)malloc(sizeof(Node));	Pop(pt, q);	result = q->number;	free(q);	if (pt->top) //pt栈内还有数字,说明表达式有误	{		return NO_RESULT;	} 	else	{		return result;	}}

整个工作完成,不过,程序要想正确运行,对输入有一些要求,比如:

 

(1)输入不能有空字符;

(2)输入的数字只能是整数,而且除数不能是0;

(3)确保中间的运算结果也都是整数,否则会舍弃小数部分,从而影响精度。

全部代码如下:

#include <STDIO.H>#include <STDLIB.H>#define MAX_EXP 100			//表达式最大长度#define MAX_INT 10			//整数最大位数#define NO_RESULT -99999	//计算异常的返回值enum node_type{ NUM, OP };struct node{	int number;	char operation;	enum node_type type;	struct node *next;};struct stack{	struct node *top;	int length;};typedef struct node Node;typedef struct stack Stack;int GetResult(const char []);void StringToMidExp(const char [], Stack *);int TenPow(int);void MidExpToBackExp(Stack *, Stack *);int BackExpToResult(Stack *);void ShowStack(const Stack *);void ShowNode(const Node *);void InitStack(Stack *);void Push(Stack *, Node *);void Pop(Stack *, Node *);void ClearStack(Stack *);void Reverse(Stack *, Stack *);int main(void){	char expression[MAX_EXP];	int result;	printf("输入四则运算表达式:\n");	scanf("%s", expression);	result = GetResult(expression);	if (result == NO_RESULT)	{		printf("表达式有误,计算失败。\n");	} 	else	{		printf("计算结果是:%d\n", result);	}	return 0;}//根据表达式的字符串计算结果int GetResult(const char exp[]){	Stack middleExp, backExp;	Stack *pm, *pb;	pm = &middleExp;	pb = &backExp;	InitStack(pm);	InitStack(pb);	StringToMidExp(exp, pm);	printf("中缀表达式:");	ShowStack(pm);	MidExpToBackExp(pm, pb);	printf("后缀表达式:");	ShowStack(pb);	return BackExpToResult(pb);}//字符串转换成中缀表达式void StringToMidExp(const char exp[], Stack *ps){	int num[MAX_INT];	int k, n, count, temp;	Stack m_stack;	Stack *pm;	Node *q;	k = 0;	count = 0;	pm = &m_stack;	InitStack(pm);	while (exp[k] != ‘\0‘)	{		if (exp[k] >= ‘0‘ && exp[k] <= ‘9‘) //数字0到9		{			count++;					//count记录整数的位数			num[count-1] = exp[k] - 48;	//num数组记录整数的每一位		}		else if ((exp[k] >= 40 && exp[k] <= 43) || exp[k] == 45 || exp[k] == 47) //运算符		{			if (count > 0) //转换该运算符之前的数字			{				n = 0;				temp = 0;				while (n < count)				{					temp += num[n] * TenPow(count - n -1); //每一位乘以10的某次方					n++;				}								q = (Node *)malloc(sizeof(Node));				q->type = NUM;				q->number = temp;				Push(pm, q);			}			count = 0; //位数清零			q = (Node *)malloc(sizeof(Node));			q->type = OP;			q->operation = exp[k];			Push(pm, q);		}		k++;	}	if (count > 0) //把最后一个数字转换出来	{		n = 0;		temp = 0;		while (n < count)		{			temp += num[n] * TenPow(count - n -1);			n++;		}		q = (Node *)malloc(sizeof(Node));		q->type = NUM;		q->number = temp;		Push(pm, q);	}	Reverse(pm, ps); //颠倒一下次序}//计算10的n次方int TenPow(int n){	if (n == 0)	{		return 1;	} 	else	{		int i, k;		i = 0;		k = 1;		while (i < n)		{			k *= 10;			i++;		}		return k;	}}//中缀表达式转换成后缀表达式void MidExpToBackExp(Stack *pm, Stack *pb){	Stack tempStack, oprStack;	Stack *pt, *pr;	Node *q, *r;	pt = &tempStack;	//临时存储后缀表达式	pr = &oprStack;		//用来决定运算符的顺序	InitStack(pt);	InitStack(pr);	while (pm->top)	{		q = (Node *)malloc(sizeof(Node));		Pop(pm, q);		if (q->type == NUM)		{			Push(pt, q);		} 		else		{			if (q->operation == ‘+‘ || q->operation == ‘-‘)			{				while (pr->top && pr->top->operation != ‘(‘)				{					r = (Node *)malloc(sizeof(Node));					Pop(pr, r);					Push(pt, r);				}				Push(pr, q);			}			else if (q->operation == ‘*‘ || q->operation == ‘/‘)			{				while (pr->top && pr->top->operation != ‘(‘ && pr->top->operation != ‘+‘ && pr->top->operation != ‘-‘)				{					r = (Node *)malloc(sizeof(Node));					Pop(pr, r);					Push(pt, r);				}				Push(pr, q);			}			else if (q->operation == ‘(‘)			{				Push(pr, q);			} 			else			{				while (pr->top)				{					if (pr->top->operation == ‘(‘)					{						r = (Node *)malloc(sizeof(Node));						Pop(pr, r);						free(r);						break;					}					r = (Node *)malloc(sizeof(Node));					Pop(pr, r);					Push(pt, r);				}				free(q);			}		}	}	while (pr->top) //栈内剩余运算符全部出栈	{		r = (Node *)malloc(sizeof(Node));		Pop(pr, r);		Push(pt, r);	}	Reverse(pt, pb); //颠倒一下次序}//根据后缀表达式计算结果int BackExpToResult(Stack *ps){	if (!ps->top) //空栈说明表达式有误	{		return NO_RESULT;	}	Stack tempStack;	Stack *pt;	Node *q;	int num_left, num_right, result;	pt = &tempStack;	InitStack(pt);	while (ps->top)	{		if (ps->top->type == NUM)		{			q = (Node *)malloc(sizeof(Node));			Pop(ps, q);			Push(pt, q);		} 		else		{			q = (Node *)malloc(sizeof(Node));			Pop(pt, q);			num_right = q->number;			free(q);			if (!pt->top) //pt栈内没有第2个数了,说明表达式有误			{				return NO_RESULT;			}			q = (Node *)malloc(sizeof(Node));			Pop(pt, q);			num_left = q->number;			free(q);			q = (Node *)malloc(sizeof(Node));			Pop(ps, q);			switch(q->operation)			{			case ‘+‘:				result = num_left + num_right;				break;			case ‘-‘:				result = num_left - num_right;				break;			case ‘*‘:				result = num_left * num_right;				break;			case ‘/‘:				result = num_left / num_right;				break;							}			free(q);			q = (Node *)malloc(sizeof(Node));			q->type = NUM;			q->number = result;			Push(pt, q);		}	}	q = (Node *)malloc(sizeof(Node));	Pop(pt, q);	result = q->number;	free(q);	if (pt->top) //pt栈内还有数字,说明表达式有误	{		return NO_RESULT;	} 	else	{		return result;	}}//显示栈中元素void ShowStack(const Stack *ps){	if (ps->top)	{		Node *p = ps->top;		while (p->next)		{			ShowNode(p);			printf(" ");			p = p->next;		}		ShowNode(p);		printf("\n");	} 	else	{		printf("无\n");	}}//显示一个节点元素void ShowNode(const Node *p){	if (p->type == NUM)	{		printf("%d", p->number);	} 	else	{		printf("%c", p->operation);	}}//初始化栈void InitStack(Stack *ps){	ps->length = 0;	ps->top = NULL;}//节点入栈void Push(Stack *ps, Node *pn){	pn->next = ps->top;	ps->top = pn;	ps->length++;}//节点出栈void Pop(Stack *ps, Node *pn){	if (ps->top)	{		Node *q = ps->top;		pn->next = NULL;		pn->number = q->number;		pn->operation = q->operation;		pn->type = q->type;		ps->top = q->next;		free(q);		ps->length--;	} 	else	{		pn = NULL;	}}//清空栈void ClearStack(Stack *ps){	Node *q;	while (ps->top)	{		q = ps->top;		ps->top = q->next;		free(q);		ps->length--;	}}//反转栈中元素的次序void Reverse(Stack *ps1, Stack *ps2){	if (ps1->top)	{		Node *q;		ClearStack(ps2);		while (ps1->top)		{			q = (Node *)malloc(sizeof(Node));			Pop(ps1, q);			Push(ps2, q);		}	} 	else	{		ps2->top = NULL;		ps2->length = 0;	}}

栈的典型应用-表达式求值【转】