(C语言)后缀表达式-整数计算

看到栈的一个有意思应用,后缀表达式,在《数据结构与算法分析C语言描述》中有提到。用来计算形如:4.99+(5.99+6.99)*1.06的结果。

栈的数组实现

 完整代码:

#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "math.h"


typedef char type;    //存储的数据类型

//结构
typedef struct stack
{
	int Top;        //模拟栈顶,-1为空
	int capacity;   //栈的容量
	type* array;    //数据本体
}stack;        //把struct stack定义别名为stack

//如果栈为空(ptr->Top为-1)返回1
int IsEmpty(stack* ptr) {
	return ptr->Top == -1;
}

//将栈顶指向空
void MakeEmpty(stack* ptr) {
	ptr->Top = -1;
}

//释放内存
void DisposeStack(stack* ptr) {
	if (ptr != NULL) {
		free(ptr->array);
		free(ptr);
	}
}

//初始化,创建num大小的栈
stack* init(int num) {
	stack* tmp = (stack*)malloc(sizeof(stack));
	if (tmp==NULL)
	{
		printf("out of stack!");
		system("pause");
	}
	tmp->array = (type*)malloc(sizeof(type) * num);
	if (tmp->array==NULL)
	{
		printf("out of array!");
		system("pause");
	}
	tmp->capacity = num;
	MakeEmpty(tmp);
	return tmp;
}

//将value压入ptr栈中
void push(stack* ptr, type value) {
	if (ptr->Top+1==ptr->capacity)
	{
		printf("out of capacity!");
		system("pause");
	}
	ptr->array[++ptr->Top] = value;
}

//获取栈指针当前所指的元素值
type GetElement(stack* ptr) {
	if (!IsEmpty(ptr)) {
		return ptr->array[ptr->Top];
	}
	return 0;
}

//删除栈元素(将top减一)
void DelElement(stack* ptr) {
	if (IsEmpty(ptr)) {
		printf("Empty stack !");
		system("pause");
	}
		ptr->Top--;
}

//退栈并返回删除的值
type pop(stack* ptr) {
	if (!IsEmpty(ptr)) {
		return ptr->array[ptr->Top--];
	}
	printf("Empty stack !");
	return 0;
}

 中缀转后缀表达式

//中缀转后缀表达式,@为每一个数值的结尾标志
char* InfixChange(char data[]) {

	static char value[500];	//要用static阻止程序释放char数组内存,不然传参访问地址会变无效

	stack* tmp = init(sizeof(data)*3);
	
	int times = 0;    //供value赋值所用

	value[times++] = '@';	//数组开头加上@
	for (int i = 0; data[i] !='\0'; i++)    //遍历中缀表达式
	{
		if (data[i] >= '0' && data[i] <= '9') {
			value[times++] = data[i];       //如果为数值就将其放入要返回的字符数组中
		}
		else if (data[i] == '*' || data[i] == '/') {
			value[times++] = '@';	//数字尾部加上@
			push(tmp, data[i]);     //如果为*或/就将其压入栈中
		}
		else if (data[i] == '+' || data[i] == '-') {
			value[times++] = '@';   //数字尾部加上@

            //如果是+或-号就判断当前栈中是否为*或/

			if (GetElement(tmp) == '*' || GetElement(tmp) == '/') {
				for (int i = tmp->Top; i >-1; i--)
				{
                //如果是*或/,就出栈,直到访问到'('或栈为空
					if (GetElement(tmp) != '(') {
                        //如果有'('就先把'('后的符号输出到value数组中
						value[times++]=pop(tmp);
					}
				}
			}
			push(tmp, data[i]);    //将当前的+或-压入栈中
		}
		else if (data[i] == '(')
		{
			push(tmp, data[i]);    //如果是'('压入栈中
		}
		else if (data[i] == ')') {
			value[times++] = '@';    //在')'前的数字后面输出@
			for (int i = tmp->Top; i !=-1; i--)
			{
				//如果遇到')'就一直出栈直到遇到'('
                if (GetElement(tmp) == '(') {
					pop(tmp);
					break;
				}
				else
					value[times++] = pop(tmp);
			}
		}
		else if (data[i] == '.') {    //如果遇到小数点就把他放到value中
			value[times++] = data[i];
		}
	}
    
    //把栈中剩余的符号输出到value中
	if (tmp->Top != -1) {
		value[times++] = '@';
		for (int i = tmp->Top; i != -1;i--) {
			value[times++] = pop(tmp);
		}
		value[times] = '\0';
		free(tmp);        //注意要释放内存
	}
	return value;    //返回值,注意定义时要用static,不然非主函数访问会读取不到值
}

后缀表达式计算值

//提取栈中字符的字面值
double getNum(stack* ptr) {
	double numInt = 0.0,fraction=0.0;
	//numInt为整数部分,fraction为小数部分
	int deep = 0,fra=0;
    //栈头不止有一个@
	while (GetElement(ptr) == '@')
		ptr->Top--;
	for (;GetElement(ptr)!='@'; pop(ptr))
	{
		if (GetElement(ptr) == '.') {    //如果遇到'.'就说明numInt所提取的部分均为小数
			fraction = numInt;    //直接赋值
			numInt = 0.0;    //重置整数值
			fra = deep;      //小数的具体位数
			deep = 0;        //整数具体位数
		}
		else if(GetElement(ptr)>='0'&&GetElement(ptr)<='9') {	//限定数字,因为数值只有尾部有@
			//输出为小数
			numInt += (GetElement(ptr)-'0');
			numInt /= 10;    //如果用*=,则输出为倒序
			++deep;	//用来计算除了多少个10
		}
	}
	numInt *= pow(10, deep);    //将小数点去掉
    
    //此处想要解决double精度缺失的问题
	if ((int)(numInt * 10) % 10 == 0)    
		return numInt + fraction;
	else
		return (int)(numInt + fraction) + 1;
	//===============================精度缺失=============================//
	//return numInt + ((int)(fraction * pow(10, fra))) / pow(10, fra);
}

//double类型转字符数组
char* dtoc(double num) {
	int integet = (int)num;    //整数部分
	double decimal = num - integet;    //小数部分

	char value[20],tmp[20];	//tmp用来存放倒序输出的值
	int count = 0;
	while (integet!=0)    //输出整数部分的值,直到整数部分为0
	{
		tmp[count++] = (integet % 10)+'0';
		integet /= 10;
	}
	tmp[count] = '\0';    //加上结束符
	int maxCount = count; //后面需要倒序输出而减去count,所以在此赋值
	for (int i = 0; i < maxCount; i++)    //将本就倒序输入的tmp值倒序输出到value中
                                          //实现正序输出
	{
		value[i] = tmp[--count];
	}
	
	//此处未设置最后一位为'\0'
	//小数
	if(decimal){    //如果小数不为0
		value[maxCount++] = '.';    //返回值里放上小数点

		decimal *= 10;			//先乘10
		while ((int)decimal != 0)	//3.所以这里改成只要个位是0就退出(没卵用)
		{
			int tmpNum = decimal;	//取整数部分给tmpNum
			value[maxCount++] = tmpNum + '0';	//赋值
			decimal -= tmpNum;	//减去整数部分
			decimal *= 10;		//进位
		}
	}
	
	value[maxCount] = '\0';	//1.写到这里,程序逻辑上没什么问题,但是在小数的精度上出现了问题
    //即在getNum中的返回值不准确
	//printf("%s", value);	//2.decimal中本应该是0.456,但是在末尾会出现极小的几个数(本应不存在),从而导致数组越界
	return value;
}

//根据读取的字符做运算
double operation(double n1, double n2, char signal) {
	if (signal == '*')    //如果signal是*就做乘法运算
		return n1 * n2;
	else if (signal == '/')
		return n1 / n2;
	else if (signal == '+')
		return n1 + n2;
	else if (signal == '-')
		return n1 - n2;
}

//计算后缀表达式的答案
char* makeSum(char data[]) {
	stack* tmp = init(sizeof(data)*10);
	char re[sizeof(data)*10];            //返回数组
	for (int i = 0; data[i] != '\0'; i++) {    //遍历数组
		double n1 = 0.0, n2 = 0.0;
		if (data[i] == '*' || data[i] == '/' || data[i] == '+' || data[i] == '-') {
			//假定离符号近的元素为n2,另一个为n1
			//注意,调用operation时n1在前n2在后,否则+或-时会出现问题
			n2 = getNum(tmp);	//1--返回值有问题为3.39999999999//精度缺失(未解决)
			n1 = getNum(tmp);   //如果计算的值为整数,则可以忽略精度的问题
			char* tmpChar=dtoc(operation(n1, n2, data[i]));    //根据符号做计算
            //double转char库里有函数,不要像我一样大冤种
			for (int i = 0; tmpChar[i]!='\0'; i++)
			{
				push(tmp, tmpChar[i]);    //把计算得到的值放入栈中
			}
			push(tmp, '@');    //数值尾部加上@符号做分隔
		}
		else
			push(tmp, data[i]);    //不是符号就直接压入栈中
	}
	tmp->Top--;	//减去尾部的'@'
    //如果栈不为空
	if (tmp->Top != -1) {	//因为tmp->array[tmp->Top]处总是'@'所以此处可以不等于-1或0
		int i = tmp->Top;   //i用来做返回数组的赋值
		re[i] = '\0';       //当tmp->Top为@时,放入'\0'
		
        //如果栈为空,或等于@,或re数据写完了(i=0),结束循环
        for (; tmp->Top != -1 && tmp->array[tmp->Top] != '@'&&i>=0; tmp->Top--)
		{
			re[--i] = tmp->array[tmp->Top];
		}
		free(tmp);    //注意释放内存
		return re;
	}
	else
	{
		free(tmp);    //出错,释放内存,返回0
		return 0;
	}
}

一些废话

double getNum(stack* ptr) {

 //此处想要解决double精度缺失的问题
    if ((int)(numInt * 10) % 10 == 0)    
        return numInt + fraction;
    else
        return (int)(numInt + fraction) + 1;
    //===============================精度缺失=============================//
    //return numInt + ((int)(fraction * pow(10, fra))) / pow(10, fra);

}

写到这里就出现了一个严重的问题:精度缺失

我的本意是getNum提取浮点数值,交给operation来计算,但经过测试发现,会出现诸如:

C语言浮点数的精度丢失_c语言精度损失原因_anewboya的博客-优快云博客icon-default.png?t=N3I4https://blog.youkuaiyun.com/m0_62853450/article/details/122439317的问题,所以代码测试输出没问题,只是二进制无法准确的存储小数。

所以标题才会为后缀表达式-整数的计算。

//如有大佬有较好的解决方法,还望指点一二,小人在此先谢过诸位。

主函数做的测试,如读者发现有错误,请指出,感激不尽。

基本数据类型的转换

 下链接为常用类型转换函数的用法

 C语言常用库函数-转换类型类_hopegrace的博客-优快云博客icon-default.png?t=N3I4https://blog.youkuaiyun.com/hopegrace/article/details/104691481

可以直接用的,就不要自己写,因为自己写的肯定没有库里自带的函数好。

不要像我一样大冤种 。

 主函数测试

void main() {

	//测试InfixChange函数

	//printf("%s", InfixChange("1.1+2*3.4+(4*5.6+66)*7"));
    //2++因为发现makeSum的传值有问题所以在这里测试

	//结果:@1.1@2@3.4@*+4@5.6@*66@+@7@*+
	//66后面的@是)处加上的,因为+号先被添加到66后面,成了66+@所以需要在)处添加@

	//测试getNum函数
	/*stack* tmp = init(10);
	push(tmp, '@');
	push(tmp, '2');
	push(tmp, '@');
	push(tmp, '3');
	push(tmp, '4');
	push(tmp, '.');
	push(tmp, '4');
	push(tmp, '5');
	push(tmp, '6');
	push(tmp, '@');
	printf("%lf", getNum(tmp));*/

	//测试double转char函数
	//dtoc(123.456);
	//printf("%s", dtoc(123.456));
	
	//测试字符占用大小
	/*char a[] = {"1+2*3+(4*5+6)*7"};
	printf("%d\n", sizeof(a));
	printf("%d\n", sizeof(a[0]));
	printf("%d\n", sizeof(a)/sizeof(a[0]));*/

	//测试makeSum函数
	//printf("%s",makeSum("@1@2@3@*+4@5@*66@+@7@*+"));	//输出609

	char aaa[100];
	scanf("%s", aaa);

	printf("%s",InfixChange("1+2*3+(4*5+66)*7"));
	//
	//printf("%s",makeSum("@1@2@3@*+4@5@*66@+@7@*+"));
	
	printf("%s",makeSum(InfixChange(aaa)));
    //1++在这里发现InfixChange(aaa)的返回值makeSum访问不到,所以上述要加上static
}

结果:

 

 完整代码

#define _CRT_SECURE_NO_WARNINGS 1

#include "stdio.h"
#include "stdlib.h"
#include "string.h"
#include "math.h"

/*===============栈===============*/
typedef char type;

typedef struct stack
{
	int Top;
	int capacity;
	type* array;
}stack;

int IsEmpty(stack* ptr) {
	return ptr->Top == -1;
}

void MakeEmpty(stack* ptr) {
	ptr->Top = -1;
}

void DisposeStack(stack* ptr) {
	if (ptr != NULL) {
		free(ptr->array);
		free(ptr);
	}
}

stack* init(int num) {
	stack* tmp = (stack*)malloc(sizeof(stack));
	if (tmp==NULL)
	{
		printf("out of stack!");
		system("pause");
	}
	tmp->array = (type*)malloc(sizeof(type) * num);
	if (tmp->array==NULL)
	{
		printf("out of array!");
		system("pause");
	}
	tmp->capacity = num;
	MakeEmpty(tmp);
	return tmp;
}

void push(stack* ptr, type value) {
	if (ptr->Top+1==ptr->capacity)
	{
		printf("out of capacity!");
		system("pause");
	}
	ptr->array[++ptr->Top] = value;
}

type GetElement(stack* ptr) {
	if (!IsEmpty(ptr)) {
		return ptr->array[ptr->Top];
	}
	return 0;
}

void DelElement(stack* ptr) {
	if (IsEmpty(ptr)) {
		printf("Empty stack !");
		system("pause");
	}
		ptr->Top--;
}

type pop(stack* ptr) {
	if (!IsEmpty(ptr)) {
		return ptr->array[ptr->Top--];
	}
	printf("Empty stack !");
	return 0;
}
/*===============栈===============*/


//中缀转后缀表达式,@为每一个数值的结尾标志
char* InfixChange(char data[]) {
	//int numvalue[sizeof(data) / sizeof(data[0])];

	static char value[500];	//要用static阻止程序释放char数组内存,不然传参访问地址会变无效

	stack* tmp = init(sizeof(data)*3);
	int times = 0;

	//printf("%d\n", sizeof(value));

	value[times++] = '@';	//数组开头加上@
	for (int i = 0; data[i] !='\0'; i++)
	{
		if (data[i] >= '0' && data[i] <= '9') {
			value[times++] = data[i];
		}
		else if (data[i] == '*' || data[i] == '/') {
			value[times++] = '@';	//数字尾部加上@
			push(tmp, data[i]);
		}
		else if (data[i] == '+' || data[i] == '-') {
			value[times++] = '@';
			if (GetElement(tmp) == '*' || GetElement(tmp) == '/') {
				for (int i = tmp->Top; i >-1; i--)
				{
					if (GetElement(tmp) != '(') {
						value[times++]=pop(tmp);
					}
				}
				//push(tmp, data[i]);
			}
			//else
				push(tmp, data[i]);
		}
		else if (data[i] == '(')
		{
			//value[times++] = '@';
			push(tmp, data[i]);
		}
		else if (data[i] == ')') {
			value[times++] = '@';
			for (int i = tmp->Top; i !=-1; i--)
			{
				if (GetElement(tmp) == '(') {
					pop(tmp);
					break;
				}
				else
					value[times++] = pop(tmp);
			}
		}
		else if (data[i] == '.') {
			value[times++] = data[i];
		}
	}
	if (tmp->Top != -1) {
		value[times++] = '@';
		for (int i = tmp->Top; i != -1;i--) {
			value[times++] = pop(tmp);
		}
		value[times] = '\0';
		free(tmp);
	}
	return value;

}

//提取栈中字符的字面值
double getNum(stack* ptr) {
	double numInt = 0.0,fraction=0.0;
	//fraction为小数部分
	int deep = 0,fra=0;
	//if (GetElement(ptr) == '@')
		//ptr->Top--;
	while (GetElement(ptr) == '@')
		ptr->Top--;
	for (;GetElement(ptr)!='@'; pop(ptr))
	{
		if (GetElement(ptr) == '.') {
			fraction = numInt;
			numInt = 0.0;
			fra = deep;
			deep = 0;
		}
		else if(GetElement(ptr)>='0'&&GetElement(ptr)<='9') {	//限定数字,因为数值只有尾部有@
			//输出为小数
			numInt += (GetElement(ptr)-'0');
			numInt /= 10;
			++deep;	//用来计算除了多少个10
		}
	}
	numInt *= pow(10, deep);
	if ((int)(numInt * 10) % 10 == 0)
		return numInt + fraction;
	else
		return (int)(numInt + fraction) + 1;
	//===============================精度缺失=============================//
	//return numInt + ((int)(fraction * pow(10, fra))) / pow(10, fra);
}

char* dtoc(double num) {
	int integet = (int)num;
	double decimal = num - integet;
	//integet为整数,decimal为小数
	char value[20],tmp[20];	//tmp用来存放倒序输出的值
	int count = 0;
	while (integet!=0)
	{
		tmp[count++] = (integet % 10)+'0';
		integet /= 10;
	}
	tmp[count] = '\0';
	int maxCount = count;
	for (int i = 0; i < maxCount; i++)
	{
		value[i] = tmp[--count];
	}
	
	//此处未设置最后一位为'\0'
	//小数
	if(decimal){
		value[maxCount++] = '.';

		decimal *= 10;			//先乘10
		while ((int)decimal != 0)	//3.所以这里改成只要个位是0就退出
		{
			int tmpNum = decimal;	//取整数部分给tmpNum
			value[maxCount++] = tmpNum + '0';	//赋值
			decimal -= tmpNum;	//减去整数部分
			decimal *= 10;		//进位
		}
	}
	
	value[maxCount] = '\0';	//1.写到这里,程序逻辑上没什么问题,但是在小数的精度上出现了问题
	//printf("%s", value);	//2.decimal中本应该是0.456,但是在末尾会出现极小的几个数(本应不存在),从而导致数组越界
	return value;
}

//根据读取的字符做运算
double operation(double n1, double n2, char signal) {
	if (signal == '*')
		return n1 * n2;
	else if (signal == '/')
		return n1 / n2;
	else if (signal == '+')
		return n1 + n2;
	else if (signal == '-')
		return n1 - n2;
}

char* makeSum(char data[]) {
	stack* tmp = init(sizeof(data)*10);
	char re[sizeof(data)*10];
	for (int i = 0; data[i] != '\0'; i++) {
		double n1 = 0.0, n2 = 0.0;
		if (data[i] == '*' || data[i] == '/' || data[i] == '+' || data[i] == '-') {
			//假定离符号近的元素为n2,另一个为n1
			//调用operation时n1在前n2在后
			n2 = getNum(tmp);	//1--返回值有问题为3.39999999999//精度缺失(未解决)
			n1 = getNum(tmp);
			char* tmpChar=dtoc(operation(n1, n2, data[i]));
			for (int i = 0; tmpChar[i]!='\0'; i++)
			{
				push(tmp, tmpChar[i]);
			}
			push(tmp, '@');
		}
		else
			push(tmp, data[i]);
	}
	tmp->Top--;	//减去尾部的'@'
	if (tmp->Top != -1) {	//因为tmp->array[tmp->Top]处总是'@'所以此处可以不等于-1或0
		int i = tmp->Top;
		re[i] = '\0';
		for (; tmp->Top != -1 && tmp->array[tmp->Top] != '@'&&i>=0; tmp->Top--)
		{
			re[--i] = tmp->array[tmp->Top];
		}
		free(tmp);
		return re;
	}
	else
	{
		free(tmp);
		return 0;
	}
}

void main() {

	//测试InfixChange函数
	//printf("%s", InfixChange("1.1+2*3.4+(4*5.6+66)*7"));
	//结果:@1.1@2@3.4@*+4@5.6@*66@+@7@*+
	//66后面的@是)处加上的,因为+号先被添加到66后面,成了66+@所以需要在)处添加@

	//测试getNum函数
	/*stack* tmp = init(10);
	push(tmp, '@');
	push(tmp, '2');
	push(tmp, '@');
	push(tmp, '3');
	push(tmp, '4');
	push(tmp, '.');
	push(tmp, '4');
	push(tmp, '5');
	push(tmp, '6');
	push(tmp, '@');
	printf("%lf", getNum(tmp));*/

	//测试double转char函数
	//dtoc(123.456);
	//printf("%s", dtoc(123.456));
	
	//测试字符占用大小
	/*char a[] = {"1+2*3+(4*5+6)*7"};
	printf("%d\n", sizeof(a));
	printf("%d\n", sizeof(a[0]));
	printf("%d\n", sizeof(a)/sizeof(a[0]));*/

	//测试makeSum函数
	//printf("%s",makeSum("@1@2@3@*+4@5@*66@+@7@*+"));	//输出609

	char aaa[100];
	scanf("%s", aaa);

	//printf("%s",InfixChange("1+2*3+(4*5+66)*7"));
	
	//printf("%s",makeSum("@1@2@3@*+4@5@*66@+@7@*+"));
	//char* tmp = InfixChange(aaa);
	printf("%s",makeSum(InfixChange(aaa)));

}
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值