zoukankan      html  css  js  c++  java
  • 栈的典型应用-表达式求值【转】

    本文转载自: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] != '')
    	{
    		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("输入四则运算表达式:
    ");
    	scanf("%s", expression);
    
    	result = GetResult(expression);
    	if (result == NO_RESULT)
    	{
    		printf("表达式有误,计算失败。
    ");
    	} 
    	else
    	{
    		printf("计算结果是:%d
    ", 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] != '')
    	{
    		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("
    ");
    	} 
    	else
    	{
    		printf("无
    ");
    	}
    }
    
    //显示一个节点元素
    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;
    	}
    }
  • 相关阅读:
    D3.js 几种常用的坐标轴
    前端页面的用户体验优化设计
    用can-fixture拦截Ajax并模拟响应
    webpack入门及使用
    CommonJS和AMD规范
    凝思6.0虚拟机搭建--遇到的问题
    凝思6.0安装vmware tools记录
    linux定时任务crontab命令
    find命令使用指南
    关于字体、字形、字符集、字体大小
  • 原文地址:https://www.cnblogs.com/zzb-Dream-90Time/p/5930460.html
Copyright © 2011-2022 走看看