数据结构:整数简易四则运算(类实现)

基本要求

用户输入一串表达式字符串,能在txt文本以及控制台输出表达式及其计算结果。

部分结果:
在这里插入图片描述

参照

参考内容为数据结构(c语言版,清华大学出版社)P52~P54

思路

总体思路为:字符串经过条件判断后,分别压入两个栈中,然后不断计算得到最终结果输出。
创建栈的结构:

//队列结构
typedef struct Sqstack
{
	char* base;
	char* top;
	int stacksize;
}SqC;


typedef struct V_Sqstack
{
	int* base;
	int* top;
	//计数器
	int stacksize;
}SqV;

基础思路

1.依次判别字符串中字符的类型:数字或运算符。
根据类型不同将字符入不同栈。
读取字符使用的语句是getchar()整个读取循环操作的基础
根据输入流不断向下读取:

//不断向下读取字符,直到读取到设置的结束字符,程序结束,得到最终成果。
char c = getchar();

判别函数:

//辅助函数:用于判断字符进栈分类
	bool InOP(char c)
	{
		if (c == '*' || '(' == c || ')' == c || '/' == c || '+' == c || '-' == c || '#' == c)
			return true;
		else
			return false;
	};

判别函数的应用:

//读取数字
		if (!op.InOP(c))
{
//对数字的操作
...
}
else
{
...//对运算符的操作
}

需要注意:
基本四则运算式的顺序并不是一致的,即并不是都从左往右或都从右往左的。因为,运算符有优先级。
例如: 3- 2*2 与 (3-2)*2 运算顺序明显不同
两个数的四则运算是固定的。 因此,可以将一个运算式分解成若干个两数的四则运算再进行组合。

例如:
a.在3-2*2中,运算符“ - ”的优先级低于“ * ”,故而,运算顺序为:
2*2 得到 4,然后3 - 4 得到 -1。
b.在(3-2)*2中,由于( )的存在,优先计算内部算式,故而,运算顺序为:
3 - 2 得到 1,然后1*2得到 2。

//计算四则运算式
	int OpOp(int b, char theta, int a)
	{
		if (theta == '*')
			return a * b;
		if (theta == '/')
			return a / b;
		if (theta == '+')
			return a + b;
		if (theta == '-')
			return a - b;

	}

由此,得到下一步思路

2.栈顶元素运算
根据运算符优先级的比较,对输入的表达式中数字进行处理。
例如:
3-2*2中,运算符“-”与“*”相比较,运算优先级为“-”小于“ * ”,因此,先处理2与2的运算。
因此,在两个栈中,总是需要根据运算符优先级比较,对栈顶元素进行操作(加入,脱出,更新)。

// 运算符优先级比较表
char OP::Precede(char c1, char c2)
{
	//+ -运算符等级
	if (c1 == '+')
		if (c2 == '+' || c2 == '-' || c2 == ')' || c2 == '#')
			return '>';
		else if (c2 == '*' || c2 == '/' || c2 == '(')
			return '<';
		else
			return ERROR;
	else if (c1 == '-')
		if (c2 == '+' || c2 == '-' || c2 == ')' || c2 == '#')
			return '>';
		else if (c2 == '*' || c2 == '/' || c2 == '(')
			return '<';
		else
			return ERROR;
	//* /运算符等级
	else if (c1 == '*')
		if (c2 == '+' || c2 == '-' || c2 == '*' || c2 == '/' || c2 == ')' || c2 == '#')
			return '>';
		else if (c2 == '(')
			return '<';
		else
			return ERROR;
	else if (c1 == '/')
		if (c2 == '+' || c2 == '-' || c2 == '*' || c2 == '/' || c2 == ')' || c2 == '#')
			return '>';
		else if (c2 == '(')
			return '<';
		else
			return ERROR;
	// ( )运算符等级
	else if (c1 == '(')
		if (c2 == ')')
			return '=';
		else if (c2 == '#')
			return ERROR;
		else
			return '<';
	else if (c1 == ')')
		if (c2 == ')')
			return ERROR;
		else
			return '>';
	else if (c1 == '#')
		if (c2 == '#')
			return '=';
		else
			return '<';
	else
		return ERROR;
}

注明:其中“#”为最后结束输入的判断字符。 相应优先级为最低。

3.输入字符串转化为整型数字
需要注意到,输入的表达式为一串字符串,而在第一步,已经完成了运算符与非运算符的判别,如今需要对类型进行转化。
可以知道的是数字 “0”在阿斯克码中排行"48"
字符型自然数转化为整型的函数可以得到:

int OP::c_to_int(char c)
{
	return int(c - '0');
}

但这种函数现在只能处理一位数字,需要得到连续数字,即大数字转化,设立循环。
因为整数的每一位都由自然数构成。
循环代码:

//将一串自然数转化为int类型数字
			int count = 0;
			//创建字符数组储存转化数字
			const int arr_max = 5;
			char c_to_i[arr_max];
			int to_i[arr_max];
			while (!op.InOP(c))
			{
				c_to_i[count] = c;
				to_i[count++] = op.c_to_int(c);
				//向下移动
				c = getchar();
				To_File(c);

			}
			//转化为数字
			int result = 0;
			for (int i = 1; i <= count; i++)
			{
				int num = to_i[count - i];
				for (int m = 1; m < i; m++)
					num *= 10;
				result += num;
			}

4.根据优先级进行的操作:
优先级根据运算符的不同分为三类:大于,小于,等于。
例如:
大于:“*”>“-”,“*”>“(”。
小于与大于正好相反。
等于:“(”=“)”。
因此根据上述第二步的优先级比较表,使用switch语句进行分支运行。

	switch (op.Precede(op.GetTop(), c))
			{
			case '<':
				op.PushStack(c); c = getchar();
				To_File(c);
				break;
			
			case'=':
				//在运算符栈中,将前括号去掉
				op.Pop(); 
				c = getchar();
				To_File(c);
				break;
			case'>':
				//记录运算符
				int op_ab =op.Pop();
				//两个数字运算
				int a = op.Pop_V();
				int b = op.Pop_V();
				//将两个数字运算后加入栈顶
				op.PushStack(op.OpOp(a, op_ab, b));
				break;
			}

注明:就如代码中注明的,不同的两个运算符比较,会对栈顶进行不同操作,分为三类:
栈顶元素的加入,运算并更改栈顶值,脱出栈顶元素。

类创建

 确认了基本思路后,开始根据思路创建函数。(由于博主更习惯于使用类解决问题,所以程序代码会有所不同。)

类基础数据

数字栈与运算符栈:

//队列结构
typedef struct Sqstack
{
	char* base;
	char* top;
	int stacksize;
}SqC;


typedef struct V_Sqstack
{
	int* base;
	int* top;
	//计数器
	int stacksize;
}SqV;

类中私有数据:

private:
	static const int MAX_SIZE = 20;
	//运算符栈
	SqC oper;
	//运算后得到值的储存栈
	SqV Value;

类中公有函数:

public:
	OP();
	~OP();
	
	//辅助函数:用于判断字符进栈分类
	bool InOP(char c)
	{
		if (c == '*' || '(' == c || ')' == c || '/' == c || '+' == c || '-' == c || '#' == c)
			return true;
		else
			return false;
	};
	//将输入加入到栈队列中
	
	//数字进入数字队列,字符进入字符队列
	void PushStack(char c);
	//数字栈
	void PushStack(int c);

	//得到栈顶元素
	char GetTop()const
	{
		if (oper.top == oper.base)
			return ERROR;
		return *(oper.top - 1);
	}

	int GetPop_V()
	{
		if (Value.top == Value.base)
			return ERROR;
		return *(Value.top - 1);
	}
	//栈顶元素脱出
	char Pop()
	{
		if (oper.top == oper.base)
			return ERROR;
		return *(--oper.top);
	}

	int Pop_V()
	{
		if (Value.top == Value.base)
			return ERROR;
		return *(--Value.top);
	}

	//运算符比较函数
	char Precede(char c, char ch);

	//辅助函数
	//将char 转化为 int
	int c_to_int(char c);
	//计算四则运算式
	int OpOp(int b, char theta, int a)
	{
		if (theta == '*')
			return a * b;
		if (theta == '/')
			return a / b;
		if (theta == '+')
			return a + b;
		if (theta == '-')
			return a - b;
	}

实现方法

  对需要用到的类函数进行实现构造。

类实现方法

构造函数:
创建运算符栈以及数字栈,并初始化类数据

OP::OP()
{
	//运算符栈
	oper.base = new char[MAX_SIZE];
	oper.top = oper.base;
	oper.stacksize = 0;

	//数字栈
	Value.base = new int[MAX_SIZE];
	Value.top = Value.base;
	Value.stacksize = 0;
}

析构函数:
不做操作。

/析构函数
OP::~OP() {};

元素加入栈顶函数:
由于两个栈只是类型不同,构造相似,故而使用函数重载

//运算符元素加入栈顶
void OP::PushStack(char c)
{
	//保障安全性
	if (oper.top - oper.base >= MAX_SIZE)
	{
		//复制原栈队列储存值,并且将栈扩容
		oper.base = (char*)realloc(oper.base, (oper.stacksize + MAX_SIZE) * sizeof(char));
		//保证安全性
		if (!oper.base)
			exit(OVERFLOW);
		oper.top = oper.base + oper.stacksize;
		oper.stacksize += MAX_SIZE;
	}
	*oper.top++ = c;
	oper.stacksize++;
}

//int 类型
void OP::PushStack(int c)
{
	if (Value.top - Value.base >= MAX_SIZE)
	{
		//复制原栈队列储存值,并且将栈扩容
		Value.base = (int*)realloc(Value.base, (Value.stacksize + MAX_SIZE) * sizeof(char));
		if (!Value.base)
			exit(OVERFLOW);
		Value.top = Value.base + Value.stacksize;
		Value.stacksize += MAX_SIZE;
	}
	*Value.top++ = c;
	Value.stacksize++;
}

转换函数:

//转化函数:将char型转化为int型
int OP::c_to_int(char c)
{
	return int(c - '0');
}

运算符比较函数:
依次对使用到的所有运算符进行优先级比较。

// 运算符优先级比较表
char OP::Precede(char c1, char c2)
{
	//+ -运算符等级
	if (c1 == '+')
		if (c2 == '+' || c2 == '-' || c2 == ')' || c2 == '#')
			return '>';
		else if (c2 == '*' || c2 == '/' || c2 == '(')
			return '<';
		else
			return ERROR;
	else if (c1 == '-')
		if (c2 == '+' || c2 == '-' || c2 == ')' || c2 == '#')
			return '>';
		else if (c2 == '*' || c2 == '/' || c2 == '(')
			return '<';
		else
			return ERROR;
	//* /运算符等级
	else if (c1 == '*')
		if (c2 == '+' || c2 == '-' || c2 == '*' || c2 == '/' || c2 == ')' || c2 == '#')
			return '>';
		else if (c2 == '(')
			return '<';
		else
			return ERROR;
	else if (c1 == '/')
		if (c2 == '+' || c2 == '-' || c2 == '*' || c2 == '/' || c2 == ')' || c2 == '#')
			return '>';
		else if (c2 == '(')
			return '<';
		else
			return ERROR;
	// ( )运算符等级
	else if (c1 == '(')
		if (c2 == ')')
			return '=';
		else if (c2 == '#')
			return ERROR;
		else
			return '<';
	else if (c1 == ')')
		if (c2 == ')')
			return ERROR;
		else
			return '>';
	else if (c1 == '#')
		if (c2 == '#')
			return '=';
		else
			return '<';
	else
		return ERROR;
}

类中的内联函数:
判断函数:

//辅助函数:用于判断字符进栈分类
	bool InOP(char c)
	{
		if (c == '*' || '(' == c || ')' == c || '/' == c || '+' == c || '-' == c || '#' == c)
			return true;
		else
			return false;
	};

返回栈顶元素:
主要用于栈顶的运算符优先级比较。

//得到栈顶元素
	char GetTop()const
	{
	//保障正确性
		if (oper.top == oper.base)
			return ERROR;
		return *(oper.top - 1);
	}

	int GetPop_V()
	{
		if (Value.top == Value.base)
			return ERROR;
		return *(Value.top - 1);
	}

栈顶元素的脱出:
用于计算过后的字符脱出栈列,以及运算符"(“和”)"的消去。

//栈顶元素脱出
	char Pop()
	{
		if (oper.top == oper.base)
			return ERROR;
		return *(--oper.top);
	}

	int Pop_V()
	{
		if (Value.top == Value.base)
			return ERROR;
		return *(--Value.top);
	}

四则运算:
两数的四则运算是组成计算式的基础。

int OpOp(int b, char theta, int a)
	{
		if (theta == '*')
			return a * b;
		if (theta == '/')
			return a / b;
		if (theta == '+')
			return a + b;
		if (theta == '-')
			return a - b;

	}

类外函数

输入文件函数:
在txt文件中展示:
其中,设立了一个静态变量,通过它控制额外输入:(计算式)

void To_File(char c)
{
	using namespace std;
	//计数器
	static int Count_V = 1;

	ofstream FileOut;
	FileOut.open("OutPut_OP.txt", ios::out | ios::app);
	if (Count_V == 1)
	{
		FileOut << "计算式:\n";
		Count_V++;
	}
	//创建录入数字
	if (!FileOut.is_open())
	{
		cout << "ERROR";
		exit(EXIT_FAILURE);
	}
	//不让结束字符输入到文件中
	if (c != '#')
		FileOut << c;

};

主函数

由思路搭建框架,函数完成具体实现

int main()
{
using namespace std;
	//创造对象
	OP op;

	//加入字符栈底
    op.PushStack('#');
	ofstream FileOut;
	FileOut.open("OutPut_OP.txt", ios::out | ios::app);
	//创建录入数字
	if (!FileOut.is_open())
	{
		cout << "ERROR";
		exit(EXIT_FAILURE);
	}

	char c = getchar();
	//将除'#'以外的字符都输入到文件中
	To_File(c);
	while (c != '#' || op.GetTop() != '#')
	{
		//读取数字
		if (!op.InOP(c))
		{
			//将一串自然数转化为int类型数字
			int count = 0;
			//创建字符数组储存转化数字
			const int arr_max = 5;
			char c_to_i[arr_max];
			int to_i[arr_max];
			while (!op.InOP(c))
			{
				c_to_i[count] = c;
				to_i[count++] = op.c_to_int(c);
				//向下移动
				c = getchar();
				To_File(c);

			}
			//转化为数字
			int result = 0;
			for (int i = 1; i <= count; i++)
			{
				int num = to_i[count - i];
				for (int m = 1; m < i; m++)
					num *= 10;
				result += num;
			}
			op.PushStack(result);
			//读取符号(已经有了getchar());
		}
		else
			//开始运算
			switch (op.Precede(op.GetTop(), c))
			{
			case '<':
				op.PushStack(c); c = getchar();
				To_File(c);
				break;
			
			case'=':
				//在运算符栈中,将前括号去掉
				op.Pop(); 
				c = getchar();
				To_File(c);
				break;
			case'>':
				//记录运算符
				int op_ab =op.Pop();
				//两个数字运算
				int a = op.Pop_V();
				int b = op.Pop_V();
				//将两个数字运算后加入栈顶
				op.PushStack(op.OpOp(a, op_ab, b));
				break;
			}
	}
	cout << "结果为:\n";
	FileOut << '='<<op.GetPop_V()<<endl;
	return op.Pop_V();
}

至此,程序完成。

程序结果截图

控制台结果输出:

在这里插入图片描述

txt结果输出保存:

在这里插入图片描述

代码仅供参考,如有错误欢迎指正,欢迎留言。

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值