看到栈的一个有意思应用,后缀表达式,在《数据结构与算法分析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的博客-优快云博客https://blog.youkuaiyun.com/m0_62853450/article/details/122439317的问题,所以代码测试输出没问题,只是二进制无法准确的存储小数。
所以标题才会为后缀表达式-整数的计算。
//如有大佬有较好的解决方法,还望指点一二,小人在此先谢过诸位。
主函数做的测试,如读者发现有错误,请指出,感激不尽。
基本数据类型的转换
下链接为常用类型转换函数的用法
C语言常用库函数-转换类型类_hopegrace的博客-优快云博客https://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)));
}