【c++栈的应用】表达式的求值

今天我们来利用栈来实现一下表达式的求值。

之前我也写过关于栈的基本功能的内容,链接如下:

数据结构之栈与队列(一)

什么是“表达式求值“

表达式的求值,简单来说,就是实现任意加减乘除与括号的表达式求值,以文本的文件的方式来输入输出。

最终效果演示

1.这里我们需要先将未计算的表达式写入begin.txt文本
在这里插入图片描述
2.运行程序
.
3.查看finall.txt文本
在这里插入图片描述

实现代码

求取算数表达式的值,我们将这个过程分成两步:

1.将算数表达式exp转化为后缀表达式postexp。
2.使用后缀表达式postexp求值。

将算数表达式exp转化为后缀表达式postexp

在转换过程中,我们采用一个运算符栈,用来存储运算符结构如下:

struct
{
	char *data;
	int top;
}op;

将中缀表达式exp转化为后缀表达式postexp的过程为:

while(从exp读取字符ch,ch!='\0')
{
if ch 为数字,将后续的所有数字均以次存放在postexp中,并以字符“#”标志数值串结束。
if ch 为'(',则将括号进栈到运算符栈op中
if ch 为')',则将运算符op中左括号右边的运算符依次出栈并放在postexp中,并将左括号删除。
if ch 为 运算符 ,当ch的优先级不大于栈顶运算符(栈顶元素为"("除外)的优先级,则以此出栈并存入postexp中,然后将ch进栈。
}

当字符串exp扫描完毕时,则将运算符栈op中的所有运算符以此出栈并以此出栈存放到postexp,最后得到后缀表达式postexp.

这里给出一个例子,以便理解:
在这里插入图片描述

将exp转化为后缀表达式postexp的trans()算法如下。

void trans(char exp[], char postexp[])
{
	char ch;
	op.data = new char[MAXSIZE];
	op.top = -1;
	int i = 0, j = 0;//i为exp下标,j为postexp下标
	
	ch = exp[i];
	//cout << ch<<"_";
	i = 1;
	//cout << ch << "_";
	while (ch!='\0')//扫描字符数组exp
	{
		switch (ch)
		{
		
		case '(':      //左括号直接入栈
			op.top++;
			op.data[op.top] = ch;
			break;
		case ')':       
			while (op.data[op.top] != '(')   //‘(’之前的字符以此出栈并存入postexp
			{
				postexp[j] = op.data[op.top];  
				j++;
				op.top--;
			}
			op.top--;  //左括号出栈,并删除
			break;
		case'+':   //符号优先级不大于栈顶任何运算符的优先级,且栈顶不是‘(’
		case'-':
			//cout << op.top << "_";
			//if ((exp[0] == '+'|| exp[0] == '-')&&(op.top==-1))//当首运算符为+或-
			//{
			//	postexp[j] = 0;  
			//	postexp[++j] = '#';
			//	j++;
			//}
			
			while (op.top != -1 && op.data[op.top] != '(')//依次出栈,存入postexp
			{
				postexp[j] = op.data[op.top];
				j++;
				op.top--;
				//cout << op.top << "_";
			}

			op.top++;             //将ch进栈
			//cout << op.top<<"_";
			op.data[op.top] = ch;
			break;
		case'*':    //符号优先级不大于栈顶任何运算符的优先级,且栈顶不是‘(’
		case'/':
			while (op.top != -1 && op.data[op.top] != '('&&
				(op.data[op.top]=='*'||op.data[op.top]=='/'))
			{
				postexp[j] = op.data[op.top];//依次出栈,存入postexp
				j++;
				op.top--;
			}
			op.top++;//将ch进栈
			op.data[op.top]=ch;
			break;
		case' ':     //将空格过滤
			break;
		default:
			while (ch >= '0' && ch <= '9')  //判定为数字
			{
				postexp[j] = ch;
				j++;
				ch = exp[i];    
				i++; 
			}
			i--;
			postexp[j] = '#'; //在每段数字结束后加‘#’作为结束符
			j++;
		}
		ch = exp[i]; 
		i++;
	}
	while (op.top != -1)
	{
		postexp[j] = op.data[op.top];
		j++;
		op.top--;
	}
	postexp[j] = '\0';

}

使用后缀表达式postexp求值

在计算后缀表达式值得过程中使用中使用一个数值栈st,设计其结构如下:

struct
{
	float* data;
	int top;
}st;
求值过程如下:
 while(从postexp读取字符ch,ch!='\0')
 {
 if ch 为数字,将后继的所有数字构成一个整数存放在数值栈st中
 if ch 为“+,则从数值栈st中退站两个运算数,相加后进栈st中
 if ch 为“-,则从数值栈st中退站两个运算数,相减后进栈st中
 if ch 为“*,则从数值栈st中退站两个运算数,相乘后进栈st中
 if ch 为“/,则从数值栈st中退站两个运算数,相除后进栈st中(若除数为0,则提示错误)
 }

我们将上一个例子得到的后缀表达式求值

在这里插入图片描述

根据上述原理得到compvalue算法

float compvalue(char postexp[])
{
	float d;
	char ch;
	int i = 0;
	st.data = new float[MAXSIZE];
	st.top = -1;
	ch = postexp[i];
	//cout << ch <<"_"<< endl;;
	i++;
	while (ch != '\0')
	{
		switch (ch)
		{
		case'+':
			st.data[st.top - 1] = st.data[st.top - 1] + st.data[st.top];
			st.top--;
			break;
		case'-':
			st.data[st.top - 1] = st.data[st.top - 1] - st.data[st.top];
			st.top--;
			break;
		case'*':
			st.data[st.top - 1] = st.data[st.top - 1] * st.data[st.top];
			st.top--;
			break;
		case'/':
			if(st.data[st.top]!=0)
			st.data[st.top - 1] = st.data[st.top - 1] / st.data[st.top];
			else
			{
				cout << ("\n\t除零错误!\n");
				exit(0);
			}
			st.top--;
			break;
		case' ':
			break;
		
		default:
			d = 0;
			while (ch >= '0' && ch <= '9')
			{
				d = 10 * d + ch - '0';    
				ch = postexp[i];
				i++;
			}
			st.top++;
			st.data[st.top] = d;
			
		}
		ch = postexp[i];
		i++;
	}
	return st.data[st.top];

}

以上就是表达式求值的核心部分。

接下来是完整的代码

1.头文件 calulate.h

#pragma once
#include<iostream>
using namespace std;
#include<fstream>
#include<string>
#include<string.h>

#define MAXSIZE 100

struct
{
	char *data;
	int top;
}op;
struct
{
	float* data;
	int top;
}st;
string read(int k);
void trans(char exp[], char postexp[]);
float compvalue(char postexp[]);
void Print(char over[]);

2.源文件calulate.cpp

#include"calulate.h"

void Init()
{
	
}
string read(int k)
{
	fstream ofs;
	ofs.open("begin.txt", ios::in|ios::out);
	if (!ofs.is_open())
	{
		cout << "文件打开失败!" << endl;
	}
	string data[100];
	int i = 0;
	while (getline(ofs, data[i]))
	{
		i++;
	}
	return data[k];


}
void trans(char exp[], char postexp[])
{
	char ch;
	op.data = new char[MAXSIZE];
	op.top = -1;
	int i = 0, j = 0;//i为exp下标,j为postexp下标
	
	ch = exp[i];
	//cout << ch<<"_";
	i = 1;
	//cout << ch << "_";
	while (ch!='\0')//扫描字符数组exp
	{
		switch (ch)
		{
		
		case '(':      //左括号直接入栈
			op.top++;
			op.data[op.top] = ch;
			break;
		case ')':       
			while (op.data[op.top] != '(')   //‘(’之前的字符以此出栈并存入postexp
			{
				postexp[j] = op.data[op.top];  
				j++;
				op.top--;
			}
			op.top--;  //左括号出栈,并删除
			break;
		case'+':   //符号优先级不大于栈顶任何运算符的优先级,且栈顶不是‘(’
		case'-':
			//cout << op.top << "_";
			//if ((exp[0] == '+'|| exp[0] == '-')&&(op.top==-1))//当首运算符为+或-
			//{
			//	postexp[j] = 0;  
			//	postexp[++j] = '#';
			//	j++;
			//}
			
			while (op.top != -1 && op.data[op.top] != '(')//依次出栈,存入postexp
			{
				postexp[j] = op.data[op.top];
				j++;
				op.top--;
				//cout << op.top << "_";
			}

			op.top++;             //将ch进栈
			//cout << op.top<<"_";
			op.data[op.top] = ch;
			break;
		case'*':    //符号优先级不大于栈顶任何运算符的优先级,且栈顶不是‘(’
		case'/':
			while (op.top != -1 && op.data[op.top] != '('&&
				(op.data[op.top]=='*'||op.data[op.top]=='/'))
			{
				postexp[j] = op.data[op.top];//依次出栈,存入postexp
				j++;
				op.top--;
			}
			op.top++;//将ch进栈
			op.data[op.top]=ch;
			break;
		case' ':     //将空格过滤
			break;
		default:
			while (ch >= '0' && ch <= '9')  //判定为数字
			{
				postexp[j] = ch;
				j++;
				ch = exp[i];    
				i++; 
			}
			i--;
			postexp[j] = '#'; //在每段数字结束后加‘#’作为结束符
			j++;
		}
		ch = exp[i]; 
		i++;
	}
	while (op.top != -1)
	{
		postexp[j] = op.data[op.top];
		j++;
		op.top--;
	}
	postexp[j] = '\0';

}
float compvalue(char postexp[])
{
	float d;
	char ch;
	int i = 0;
	st.data = new float[MAXSIZE];
	st.top = -1;
	ch = postexp[i];
	//cout << ch <<"_"<< endl;;
	i++;
	while (ch != '\0')
	{
		switch (ch)
		{
		case'+':
			st.data[st.top - 1] = st.data[st.top - 1] + st.data[st.top];
			st.top--;
			break;
		case'-':
			st.data[st.top - 1] = st.data[st.top - 1] - st.data[st.top];
			st.top--;
			break;
		case'*':
			st.data[st.top - 1] = st.data[st.top - 1] * st.data[st.top];
			st.top--;
			break;
		case'/':
			if(st.data[st.top]!=0)
			st.data[st.top - 1] = st.data[st.top - 1] / st.data[st.top];
			else
			{
				cout << ("\n\t除零错误!\n");
				exit(0);
			}
			st.top--;
			break;
		case' ':
			break;
		
		default:
			d = 0;
			while (ch >= '0' && ch <= '9')
			{
				d = 10 * d + ch - '0';    
				ch = postexp[i];
				i++;
			}
			st.top++;
			st.data[st.top] = d;
			
		}
		ch = postexp[i];
		i++;
	}
	return st.data[st.top];

}

3.测试文件test.cpp

#include"calulate.h"

void test()
{
	fstream fst;
	fst.open("final.txt", ios::app);

	char*postexp;
	postexp = new char[20];
	int i = 0;
	int j = 0;
	float tiv=0;
	string s;
	char mine[20]="0";
	for (i = 0; i < 20; i++)
	{
		if (read(i) != "")
		{
			s = read(i);
		           
					strncpy_s(mine, s.c_str(), s.length()-1);
					//当首元素为运算符时
					if (mine[0] == '-'||mine[0]=='+')
					{
						for (int m = 18; m > -1; m=m - 1)
						{
							mine[m + 1] = mine[m];
						}
						mine[0] = '0';
					}

					//当出现(-x)时
					for (int y = 1; y < 20; y++)
					{
						if (mine[y] == '-'&&mine[y-1]=='(')
						{
							for (int w = 18; w > y - 1; w = w - 1)
							{
								mine[w + 1] = mine[w];
							}
							mine[y] = '0';
						}
					}
					trans(mine, postexp);


	//char postexp[20] = "0#1#-2#+";
					tiv=compvalue(postexp);
					//cout <<endl<< postexp;
					if (mine[0] != '0')
					{
						for (j = 0; mine[j];j++)
						{
							cout << mine[j];
							fst << mine[j];
						}
					}
					if(mine[0]=='0')
					{
						for (j = 1; mine[j]; j++)
						{
							cout << mine[j];
							fst << mine[j];
						}
					}
                    
					cout <<"="<< tiv << endl;
					fst << "=" << tiv << endl;
		}
		

	}
	
}
int main()
{
	test();
	return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Ornamrr

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值