思路
通过栈这种数据结构来实现四则运算表达式的计算,是C语言这门课程中一项经典的作业。大一刚上C语言这门课的时候老师给我们的期末大作业就是这道题。总的来说我们需要做的最主要的一项工作就是把中缀表达式转为后缀表达式,这就需要用到栈中数据“先进后出”的特性。中缀、后缀表达式的概念这里不再赘述,后文运算结果截图也有展示。栈有两种形式:顺序储存结构和链式储存结构。顺序储存结构代码相对简单,不那么抽象,但是会造成一些内存浪费。而链式储存结构相对节省内存,通过malloc函数动态分配内存,用多少分配多少,但对应的代码会长一些,也稍微难理解一些。本文采用链式储存结构,旨在为大家提供一种不同的思路,同时在这个过程中可以帮助我们复习一些链表,指针等相关知识,提升C语言的基础知识的掌握能力。
代码
计算的运算表达式越复杂(多层括号),代码写的就要越完善,才能经得住广大“用户”的测试。本段代码也只实现了一部分表达式的测试,还有很大的改进空间。
#include <stdio.h>
#include <stdlib.h>
#include<string.h>
#define SIZE 100
typedef struct StackNode//中转后缀栈节点
{
char elem;
int level;
struct StackNode* next;
}StackNode;
typedef struct StackNode1//后缀运算栈节点
{
double elem;
struct StackNode1* next;
}StackNode1;
StackNode* top = NULL;//中转后缀栈顶指针
StackNode1* top1 =NULL;//后缀运算时的指针
//转换优先级
int Judge(char s)
{
int l = 0;
switch (s)
{
case '+':
{
l= 1;
break;
}
case '-':
{
l= 1;
break;
}
case '*':
{
l= 2;
break;
}
case '/':
{
l= 2;
break;
}
}
return l;
}
//PUSH
int PUSH(StackNode** top2, char e)//中缀转后缀栈
{
StackNode* s = (StackNode*)malloc(sizeofStackNode));
s->elem= e;
s->level= Judge(e);
s->next= *top2;
*top2 = s;
return 1;
}
int PUSH1(StackNode1** top2, double e)//后缀运算压栈
{
StackNode1* s = (StackNode1*)malloc(sizeof(StackNode1));
s->elem= e;
s->next= *top2;
*top2 = s;
return 1;
}
//POP
int POP(StackNode** top2, char* e)//中缀转后缀出栈
{
StackNode* p;
*e = top->elem;
p= *top2;
*top2 = top->next;
free(p);
return 1;
}
int POP1(StackNode1** top2, double* e)//后缀运算出栈
{
StackNode1* p;
*e = top1->elem;
p= *top2;
*top2 = top1->next;
free(p);
return 1;
}
//储存算数表达式
int GetExpression(char* exp)
{
printf("请输入要计算的表达式:\n");
gets_s(exp, SIZE);
return 1;
}
//中缀转后缀
void
MtoAfter(StackNode* top1, char* exp, char result[40])//char *result
{
int len = strlen(exp);
int le = 0;
int numorder = 0;
char nouse;
for (int i = 0; i < len;i++)
{
if (exp[i] == '(' || exp[i] == ')' || exp[i] == '+' || exp[i] == '-' || exp[i] == '*' || exp[i] == '/')
{
if(exp[i] == '(' || exp[i] == ')')
{
switch (exp[i])
{
case '(':
{
PUSH(&top,exp[i]);
break;
}
case ')':
{
do
{
POP(&top,&result[numorder]);//
numorder++;
}while (top->elem != '(');
POP(&top,&nouse);
if (i == len - 1 )
{
POP(&top,&result[numorder]);
numorder++;
if (top != NULL)//判断是否全部弹出
{
POP(&top,&result[numorder]);
}
}
break;
}
}
}
else
{
if (top1 == NULL)//+
{
PUSH(&top,exp[i]);
top1 = top;
}
else
{
le= Judge(exp[i]);
if (le >top->level)
{
PUSH(&top,exp[i]);//*
}
else
{
POP(&top,&result[numorder]);//* /
numorder++;
if (top == NULL)//断是否全部弹出
{
PUSH(&top,exp[i]);
}
else
{
if (le >top->level)
{
PUSH(&top,exp[i]);//
}
else
{
POP(&top,&result[numorder]);//+1+4*3/2-5
numorder++;
PUSH(&top,exp[i]);
}
}
}
}
}
}
else
{
result[numorder] = exp[i];
numorder++;
if (i == len - 1)
{
POP(&top,&result[numorder]);
numorder++;
if (top != NULL)//判断是否全弹出
{
POP(&top,&result[numorder]);
}
}
}
}
printf("Success!\n");
printf("转换后的后缀表达式为 :\n");
}
//用后缀表达式来计算
double SuffixCalculate(char result[SIZE])
{
int len = strlen(result);
double num1, num2,numresult;
num1 = num2 = numresult = 0;
char c[SIZE] = { "\0" };
int flag = 0;
for (int i = 0; i < len;i++)
{
if(result[i] == ' ')
{
switch(i -flag)
{
case 1://整数
{
break;
}
case 2:
{
POP1(&top1,&num1);
POP1(&top1,&numresult);
numresult = numresult + num1 * 0.1;
break;
}
case 3://小数点后两位
{
POP1(&top1,&num1);
POP1(&top1,&num2);
POP1(&top1, &numresult);
numresult = numresult + num2 * 0.1 + num1 * 0.01;
PUSH1(&top1,numresult);
break;
}
}
continue;
}
if (result[i] == '(' || result[i] == ')' || result[i] == '+' || result[i] == '-' || result[i] == '*' || result[i] == '/')
{
switch (result[i])
{
case '+':
{
POP1(&top1,&num1);
POP1(&top1,&num2);
numresult = num1 + num2;
PUSH1(&top1,numresult);
break;
}
case '-':
{
POP1(&top1,&num1);
POP1(&top1,&num2);
numresult = num2 - num1;
PUSH1(&top1,numresult);
break;
}
case '*':
{
POP1(&top1,&num1);
POP1(&top1,&num2);
numresult = num1 * num2;
PUSH1(&top1,numresult);
break;
}
case '/':
{
POP1(&top1,&num1);
POP1(&top1,&num2);
numresult = num2 / num1;
PUSH1(&top1,numresult);
break;
}
}
}
else
{
if(result[i] == '.')
{
flag= i;//转换浮点数的标志
continue;
}
else
{
c[0]= result[i];
PUSH1(&top1,atof(c));
}
}
}
printf("运算结果为:\n");
return numresult;
}
int main()
{
char exp[SIZE];
GetExpression(exp);
char result[SIZE] = { "\0" };
MtoAfter(top,exp, result);
char zlresult[SIZE] = { "\0" };//将转换后的表达式标准化
int j = 0;
for (int i = 0; i < SIZE; i++)
{
if (result[i] == ' ' && result[i+ 1] == ' ')
{
zlresult[j] = result[i];
continue;
}
else
{
zlresult[j] = result[i];
j++;
}
}
puts(zlresult);
printf("\n");
double finalresult = SuffixCalculate(zlresult);
printf("%lf\n",finalresult);
return 0;
}
结果和总结
测试案例
- 5+2*3-9/3
- 1+ (5-3) *3+6/2
- 3.66 +3*((6+5)/2)
总结
- 使用malloc函数动态分配内存的同时,一定要在程序结束后把内存释放掉,也就是使用free函数。在释放内存的时候涉及一个概念:指针的指针。只有使用在传递指针的指针的参数才可以真正把内存释放掉。
- 链栈其实就是链表的一种形式,只是栈只在一端进行数据的操作。虽然这样的操作会有些复杂,但是在学习知识方面可以帮助我们加深理解,熟练运用。
- 这个程序并不完善,只实现了一些基本功能(不建议直接拿去交作业哦因为老师给我的评分并不高つ﹏⊂)。所以希望大家可以一起来修改,一起把它变得更完善。
希望对你有帮助!