数据结构(C语言)栈的应用——编写一个四则运算计算器(输入数学公式得出计算结果)

数据结构(C语言)栈的应用——编写一个四则运算计算器(输入数学公式得出计算结果)


前言

栈真的很神奇当你第一次学到它的时候会觉得就这?一个结构体中定义了一个数组,和一个整型变量指向数组,实在是想不出它有什么作用,存储数据用数组和链表不香吗?直到发现它在计算器中的应用才发现栈是永远的神!


一、需要用到的知识点


1.栈的特点

定义一个栈

struct
	{
		char data[MaxSize];  //储存数据的数组,MaxSize是栈储存最大容量
		int top;             //int 类型的“指针”
	} op;                    //用top来对data中数组的操作

2.要先明白怎么让计算机认得你输入的数学表达式

比如你在键盘上输入(7+6×3)/(2+3)这个全部可以用字符串数组来保存,将运算符与数字分离出也不太难,正真要费点脑子的是搞清楚各种符号的优先级我们人当然知道在上述公式中6×3优先于7+6而括号中的(7+6×3)与(2+3)优先于/ 所以怎么让计算机理解这个是本程序的最大难点。

2.1解决方案——将表达式转为后缀表达式

同样上述例子将其转换为7#6#3#×+2#3+/ 用#分隔数字将运算符号后置,保存在一个栈中,设计一个算法只运算前两位将结果又储存进栈 (这个栈就暂定义为op),就可以实现运算符号优先级运算了。
这里参考“数据结构教程”:


二、有了理论知识后我们开搞


1.准备工作

这里定义了两个特殊的结构体数组,左结构体数组表示栈op的顶元素(op.data[op.top])中的运算字符的优先级(简单来说就是用先储存进来的运算符与后储存进来的运算符做比较)右结构体数组表示exp(后进来的运算符)

#include <iostream>
#include <stdio.h>
#include <stdlib.h>

#define MaxSize 30

struct          
{				//设定运算符优先级
	char ch;    //运算符
	int pri;    //优先级
}leftPri[] = { {'=', 0}, {'(', 1}, {'*', 5}, {'/', 5}, {'+', 3}, {'-', 3}, {')', 6} },
rightPri[] = { {'=', 0}, {'(', 6}, {'*', 4}, {'/', 4}, {'+', 2}, {'-', 2}, {')', 1} };
//rightPri[]与leftPri[]表示运算符相同时优先级会比低的数学意义是“先左后右”

2.大致的实现过程

int LeftPri(char op);      //左运算符op的优先级
int RightPri(char op);     //右运算符op的优先级
bool InOp(char ch);        //判断ch是否为运算符
int ConpareCode(char op1, char op2);  //比较op1与op2的优先级
void Transform(char* exp, char postExp[]); //将exp(用户输入的表达式)转变成栈中元素,通过栈的特性将表达式转换为后缀表达式并存入postExp[]中
float Compute(char* postExp);           //进行后缀表达式运算,返回结果


int main()
{
	char exp[30];
	printf("计算器:\n\n");
	printf("请输入数学计算公式:");
	scanf("%s", exp);
	char postExp[MaxSize];
	Transform(exp, postExp);
	printf("答案:%g\n\n", Compute(postExp));

	return 0;
}

3.个函数实现的详细步骤

3.1: int LeftPri(char op); //左运算符op的优先级和 int RightPri(char op); //右运算符op的优先级

总的来说:就是输入一个运算符返回与之对应的优先级数

int LeftPri(char op)      //左运算符op的优先级
{
	for(int i = 0; i < 7; i++)  //遍历leftPri[]数组,找到与之对应的优先级并返回
	if (leftPri[i].ch == op)
		return leftPri[i].pri;

}

int RightPri(char op)     //右运算符op的优先级
{
	int i;
	for (int i = 0; i < 7; i++)
		if (rightPri[i].ch == op)
			return rightPri[i].pri;
}

3.2: bool InOp(char ch) //判断ch是否为运算符

一个小组件函数

bool InOp(char ch)        //判断ch是否为运算符
{
	if (ch == '(' || ch == ')' || ch == '+' || ch == '-' || ch == '*' || ch == '/')
		return true;
	else
		return false;
}

3.3:int ConpareCode(char op1, char op2); //比较op1与op2的优先级

一个比较小组件函数

int ConpareCode(char op1, char op2)
{
	if (LeftPri(op1) == RightPri(op2))
		return 0;
	else if (LeftPri(op1) < RightPri(op2))
		return -1;
	else
		return 1;
}

3.4:void Transform(char* exp, char postExp[]); //将exp(用户输入的表达式)转变成栈中元素,通过栈的特性将表达式转换为后缀表达式并存入postExp[]中

这个函数可以算是这个程序最复杂的,一定要认真看才能看的懂

void Transform(char* exp, char postExp[])
{
	struct    //建立一个临时的栈,
	{
		char data[MaxSize];
		int top;
	} op;
	int i = 0;
	int flag = 0;
	op.top = -1; //令其指向空
	op.top++;
	op.data[op.top] = '=';   //top = 0,将栈顶存入“=”做为结束的标语
	while (*exp != '\0')
	{
		if (!InOp(*exp))   //将数字与运算符号分开
		{
			while (*exp >= '0' && *exp <= '9')
			{
				postExp[i++] = *exp;
				exp++;        
			}
			postExp[i++] = '#'; //在每个数字后加一个“#”将连续的数字分开
		}
		else
		{
			switch (ConpareCode(op.data[op.top], *exp)) //注意每次都跟栈顶比
			{
			case -1:           //*exp中的运算符优先级高将它入栈
				op.top++;     //top = 1
				op.data[op.top] = *exp;
				exp++;        //继续遍历
				break;
			case 0:           //只有“=”和“(”才会出现这种情况,
				op.top--; //top = 0   //如果先前碰到了就将原有的运算符退栈,可以认为将(+)中的+输入到poseExp中
				exp++;
				break;
			case 1:
				postExp[i++] = op.data[op.top];  // 如果栈顶优先级高就直接输入进postExp
				op.top--;  
				break;
			}
		}
		
	}
	while (op.data[op.top] != '=') //减到0之后就退出,
	{

		postExp[i++] = op.data[op.top];
		op.top--;  //top的值越大,优先级就越高!
	}
	postExp[i] = '\0'; //表字符串结束
}

3.5:float Compute(char * postExp) //进行后缀表达式运算,返回结果

float Compute(char * postExp)
{
	struct
	{
		float data[MaxSize];   //定义一个载体栈
		int top;
	} ser;

	float a, b, c, d;
	ser.top = -1;
	while (*postExp != '\0')
	{
		switch (*postExp)   //进行判断运算字符
		{
			case '+':
				a = ser.data[ser.top];
				ser.top--;
				b = ser.data[ser.top];
				ser.top--;
				c = a + b;
				ser.top++;
				ser.data[ser.top] = c;  //将结果存入原来 b 值的位置
				break;
			case '-':
				a = ser.data[ser.top];
				ser.top--;
				b = ser.data[ser.top];
				ser.top--;
				c = b - a;
				ser.top++;
				ser.data[ser.top] = c;
				break;
			case '*':
				a = ser.data[ser.top];
				ser.top--;
				b = ser.data[ser.top];
				ser.top--;
				c = b * a;
				ser.top++;
				ser.data[ser.top] = c;
				break;
			case '/':
				a = ser.data[ser.top];
				ser.top--;
				b = ser.data[ser.top];
				ser.top--;
				if (a!= 0)   //除数不能为零
				{
					c = b / a;
					ser.top++;
					ser.data[ser.top] = c;
				}
				else
				{
					printf("\n\t格式错误");
					exit(0);
				}
				
				break;
			default:
				d = 0;
				while (*postExp >= '0' && *postExp <= '9')
				{
					d = 10 * d + *postExp - '0'; //将数字剥离出来
					postExp++;               //两个postExp++恰好跳过了‘#’符号
				}
				ser.top++;
				ser.data[ser.top] = d;  
				
		}
		postExp++;  //循环继续
	}
	return(ser.data[ser.top]);  返回结果
}

4. 最终效果

输入公式
在这里插入图片描述
输出结果
在这里插入图片描述


总结


程序的基本步骤为:输入表达式—>转换为后缀表达式—>计算—>输出。为实现看似简单的数学基本运算C语言的代码行数竟干到了200行,而其中函数几个功能对于新手来说比较硬核,但当你搞明白了之后你就会发现支持这个计算器底层运行规律的不是代码而是数学
万物基于C语言(×)
万物基于数学(√)


附录(全部的源代码)




#include <iostream>
#include <stdio.h>
#include <stdlib.h>

#define MaxSize 30

struct          //设定运算符优先级
{				//运算符
	char ch;    //优先级
	int pri;
}leftPri[] = { {'=', 0}, {'(', 1}, {'*', 5}, {'/', 5}, {'+', 3}, {'-', 3}, {')', 6} },
rightPri[] = { {'=', 0}, {'(', 6}, {'*', 4}, {'/', 4}, {'+', 2}, {'-', 2}, {')', 1} };

int LeftPri(char op);      //左运算符op的优先级
int RightPri(char op);     //右运算符op的优先级
bool InOp(char ch);        //判断ch是否为运算符
int ConpareCode(char op1, char op2);  //比较op1与op2的优先级
void Transform(char* exp, char postExp[]); //将exp(用户输入的表达式)转变成栈中元素,
float Compute(char* postExp);           //进行后缀表达式运算,返回结果


int main()
{
	char exp[30];
	printf("计算器:\n\n");
	printf("请输入数学计算公式:");
	scanf("%s", exp);  //在不同编译器中可能报错,vs中可写成scanf_s("%s",exp,n) n建议取30.
	char postExp[MaxSize];
	Transform(exp, postExp);
	printf("答案:%g\n\n", Compute(postExp));

	return 0;
}


int LeftPri(char op)      //左运算符op的优先级
{
	for(int i = 0; i < 7; i++)
	if (leftPri[i].ch == op)
		return leftPri[i].pri;

}

int RightPri(char op)     //右运算符op的优先级
{
	int i;
	for (int i = 0; i < 7; i++)
		if (rightPri[i].ch == op)
			return rightPri[i].pri;
}

bool InOp(char ch)        //判断ch是否为运算符
{
	if (ch == '(' || ch == ')' || ch == '+' || ch == '-' || ch == '*' || ch == '/')
		return true;
	else
		return false;
}

int ConpareCode(char op1, char op2)
{
	if (LeftPri(op1) == RightPri(op2))
		return 0;
	else if (LeftPri(op1) < RightPri(op2))
		return -1;
	else
		return 1;
}

void Transform(char* exp, char postExp[])
{
	struct
	{
		char data[MaxSize];
		int top;
	} op;
	int i = 0;
	int flag = 0;
	op.top = -1;
	op.top++;
	op.data[op.top] = '=';   //top = 0
	while (*exp != '\0')
	{
		if (!InOp(*exp))
		{
			while (*exp >= '0' && *exp <= '9')
			{
				postExp[i++] = *exp;
				exp++;        
			}
			postExp[i++] = '#';
		}
		else
		{
			switch (ConpareCode(op.data[op.top], *exp)) //top = 0即op.data[op.top] = '='
			{
			case -1:
				op.top++;     //top = 1
				op.data[op.top] = *exp;
				exp++;
				break;
			case 0:
				op.top--; //top = 0
				exp++;
				break;
			case 1:
				postExp[i++] = op.data[op.top]; 
				op.top--;  
				break;
			}
		}
		
	}
	while (op.data[op.top] != '=') //减到0之后就退出,
	{

		postExp[i++] = op.data[op.top];
		op.top--;  //top的值越大,优先级就越高!
	}
	postExp[i] = '\0';
}

float Compute(char * postExp)
{
	struct
	{
		float data[MaxSize];
		int top;
	} ser;

	float a, b, c, d;
	ser.top = -1;
	while (*postExp != '\0')
	{
		switch (*postExp)
		{
			case '+':
				a = ser.data[ser.top];
				ser.top--;
				b = ser.data[ser.top];
				ser.top--;
				c = a + b;
				ser.top++;
				ser.data[ser.top] = c;
				break;
			case '-':
				a = ser.data[ser.top];
				ser.top--;
				b = ser.data[ser.top];
				ser.top--;
				c = b - a;
				ser.top++;
				ser.data[ser.top] = c;
				break;
			case '*':
				a = ser.data[ser.top];
				ser.top--;
				b = ser.data[ser.top];
				ser.top--;
				c = b * a;
				ser.top++;
				ser.data[ser.top] = c;
				break;
			case '/':
				a = ser.data[ser.top];
				ser.top--;
				b = ser.data[ser.top];
				ser.top--;
				if (a!= 0)
				{
					c = b / a;
					ser.top++;
					ser.data[ser.top] = c;
				}
				else
				{
					printf("\n\t格式错误");
					exit(0);
				}
				
				break;
			default:
				d = 0;
				while (*postExp >= '0' && *postExp <= '9')
				{
					d = 10 * d + *postExp - '0';
					postExp++;               //两个postExp++恰好跳过了‘#’符号
				}
				ser.top++;
				ser.data[ser.top] = d;
				
		}
		postExp++;
	}
	return(ser.data[ser.top]);
}



评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值