栈真的很神奇当你第一次学到它的时候会觉得就这?一个结构体中定义了一个数组,和一个整型变量指向数组,实在是想不出它有什么作用,存储数据用数组和链表不香吗?直到发现它在计算器中的应用才发现栈是永远的神!
定义一个栈
struct
{
char data[MaxSize]; //储存数据的数组,MaxSize是栈储存最大容量
int top; //int 类型的“指针”
} op; //用top来对data中数组的操作
比如你在键盘上输入(7+6×3)/(2+3)这个全部可以用字符串数组来保存,将运算符与数字分离出也不太难,正真要费点脑子的是搞清楚各种符号的优先级我们人当然知道在上述公式中6×3优先于7+6而括号中的(7+6×3)与(2+3)优先于/ 所以怎么让计算机理解这个是本程序的最大难点。
同样上述例子将其转换为7#6#3#×+2#3+/ 用#分隔数字将运算符号后置,保存在一个栈中,设计一个算法只运算前两位将结果又储存进栈 (这个栈就暂定义为op),就可以实现运算符号优先级运算了。
这里参考“数据结构教程”:
这里定义了两个特殊的结构体数组,左结构体数组表示栈op的顶元素(op.data[op.top])中的运算字符的优先级(简单来说就是用先储存进来的运算符与后储存进来的运算符做比较)右结构体数组表示exp(后进来的运算符)
#include
#include
#include
#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[]表示运算符相同时优先级会比低的数学意义是“先左后右”
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;
}
总的来说:就是输入一个运算符返回与之对应的优先级数
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;
}
一个小组件函数
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)) //注意每次都跟栈顶比
{
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'; //表字符串结束
}
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]); 返回结果
}
程序的基本步骤为:输入表达式—>转换为后缀表达式—>计算—>输出。为实现看似简单的数学基本运算C语言的代码行数竟干到了200行,而其中函数几个功能对于新手来说比较硬核,但当你搞明白了之后你就会发现支持这个计算器底层运行规律的不是代码而是数学
万物基于C语言(×)
万物基于数学(√)
#include
#include
#include
#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);
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]);
}