计算器支持括号嵌套,小数输入。
思路:
中缀表达式转后缀表达式,然后使用逆波兰计算法计算。
具体实现:
输入一个表达式,扫描该表达式,遇到数直接压入数字栈,遇到符号压入符号栈(判断优先级)。符号栈弹出符号的时候,拿数字栈上边两个元素做该符号的运算,计算出的结果再次压入数字栈。直到scanf 读取到 '#' 时候,扫描结束,此时把栈内剩余的符号弹出,再次进行计算,最后放在数字栈栈底的元素就是该表达式的结果。
注:如果想看中缀表达式转后缀表达式的具体实现,请看上一章内容。
数字拼接问题:
先说一下我使用的方法吧,定义一个字符型变量,记录上一个字符,如果上一个字符是数字的话那就说明这是一个二位数,因为上一个数字已经入数字栈了(此时这个数是一个double类型的数),把上一个数字 * 10 + 当前数字,就完成了两位数的拼接。
小数判断问题:
初始化两个个静态变量 flag(用来判断是否为小数)、cnt(用来计算目前这个数是小数后的第几位)。如过扫描到的字符是一个小数点,则 令 flag = 1,说明这个数是一个小数,扫描到下一个数字的时候 cnt ++; 说明这是小数后的第一位,然后进行数字拼接,数字栈的栈顶元素 + 当前数字/pow(10, cnt); 依次类推。直到扫描到符号的时候,令 flag = cnt = 0;
具体代码实现:
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#define OK 1
#define DEFEAT 0
#define STACK_INIT_SIZE 15
typedef int Status;
// 这个栈存放运算符
typedef struct sqStack
{
char *top;
char *base;
int stack_size;
} sqStack;
// 这个栈存放待运算的数
typedef struct resStack
{
double *top;
double *base;
int stack_size;
} resStack;
void initSqStack(sqStack *stack);
void initResStack(resStack *stack);
void ePush(sqStack *symbol_stack, char e);
void resStackCalc(resStack *res_stack, char e);
void point_cnn(resStack *res_stack, char e, int cnt);
void highPriority(sqStack *symbol_stack, resStack *res_stack);
void destroyStack(sqStack *symbol_stack, resStack *res_stack);
void res_stack_push(resStack *res_stack, char e, char previous);
void divideStack(sqStack *symbolStack, resStack *res_stack, char e);
void lowPriority(sqStack *symbol_stack, resStack *res_stack,char e);
void mediumPriority(sqStack *symbol_stack, resStack *res_stack, char e);
void symbolPriority(sqStack *symbol_stack, resStack *res_stack, char e);
void symbol_stack_push(sqStack *symbol_stack, resStack *res_stack, char e);
// 初始化符号栈
void initSqStack(sqStack *stack)
{
stack->base = (char *)malloc(STACK_INIT_SIZE * sizeof(char));
if (!stack->base)
{
printf("sqStack初始化失败!\n");
exit(1);
}
stack->top = stack->base;
stack->stack_size = STACK_INIT_SIZE;
}
// 初始化数字栈
void initResStack(resStack *stack)
{
stack->base = (double *)malloc(STACK_INIT_SIZE * sizeof(double));
if (!stack->base)
{
printf("resStack初始化失败!\n");
exit(1);
}
stack->top = stack->base;
stack->stack_size = STACK_INIT_SIZE;
}
// 区分符号与数字
void divideStack(sqStack *symbol_stack, resStack *res_stack, char e)
{
static Status flag = 0; // 记录前边有没有小数点
static int cnt = 0; // 记录小数点后数字的个数
static char previous; // 记录上一个字符
if (e == 46)
{
flag = 1;
return;
}
if (e >= 48 && e <= 57)
{
if (flag) // 如果为 1 则前面有小数点。
{
cnt ++;
point_cnn(res_stack, e, cnt);
}
else
{
res_stack_push(res_stack, e, previous);
}
}
else
{
flag = cnt = 0;
symbol_stack_push(symbol_stack, res_stack, e);
}
previous = e;
}
// 把能直接压栈的情况全写在这里
void symbol_stack_push(sqStack *symbol_stack, resStack *res_stack, char e)
{
// 如果栈为空或者当前符号为左括号,则直接压栈
if (symbol_stack->top == symbol_stack->base || e == '(')
{
*(symbol_stack->top) = e;
symbol_stack->top ++;
}
// 如果栈顶符号为左括号,则直接压栈
else if (*(--symbol_stack->top) == '(')
{
symbol_stack->top ++;
*(symbol_stack->top) = e;
symbol_stack->top ++;
}
else
{
symbol_stack->top ++;
symbolPriority(symbol_stack, res_stack, e);
}
}
// 判断符号优先级
void symbolPriority(sqStack *symbol_stack, resStack *res_stack, char e)
{
/*
既然已经进到 symbolPriority 函数了,
那就说明栈不为空,栈顶符号不为左括号,
当前符号不是左括号
*/
if (e == '+' || e == '-')
{
lowPriority(symbol_stack, res_stack, e);
}
else if (e == '*' || e == '/')
{
mediumPriority(symbol_stack, res_stack, e);
}
else // e 为 右括号的情况
{
highPriority(symbol_stack, res_stack);
}
}
// 清空括号内的符号
void highPriority(sqStack *symbol_stack, resStack *res_stack)
{
do
{
symbol_stack->top --;
if (*(symbol_stack->top) == '(')
{
break;
}
resStackCalc(res_stack, *(symbol_stack->top));
} while (symbol_stack->top != symbol_stack->base);
}
// 高优先级符号入栈
void mediumPriority(sqStack *symbol_stack, resStack *res_stack, char e)
{
do
{
symbol_stack->top --;
if (*(symbol_stack->top) == '(')
{
break;
}
// 防止栈底符号被覆盖
if (*(symbol_stack->top) == '+' || *(symbol_stack->top) == '-')
{
symbol_stack->top ++;
*(symbol_stack->top) = e;
symbol_stack->top ++;
return;
}
resStackCalc(res_stack, *(symbol_stack->top));
} while (symbol_stack->top != symbol_stack->base);
// 将优先级大于等于 e 的弹出之后压入栈中。
ePush(symbol_stack, e);
}
// 低优先级符号入栈
void lowPriority(sqStack *symbol_stack, resStack *res_stack, char e)
{
do
{
symbol_stack->top --;
if (*(symbol_stack->top) == '(')
{
break;
}
resStackCalc(res_stack, *(symbol_stack->top));
} while (symbol_stack->top != symbol_stack->base);
// 将优先级大于等于 e 的弹出之后压入栈中。
ePush(symbol_stack, e);
}
/*
把栈内优先级大于等于e的符号弹完后,进入这个函数只有两种情况,
1. 遇到左括号 (需要top指针先++ 再压栈)
2. 到栈底了 (直接压栈)
*/
void ePush(sqStack *symbol_stack, char e)
{
if (symbol_stack->top == symbol_stack->base) // 栈底
{
*(symbol_stack->top) = e;
symbol_stack->top ++;
}
else // 左括号
{
symbol_stack->top ++;
*(symbol_stack->top) = e;
symbol_stack->top ++;
}
}
// 执行运算
void resStackCalc(resStack *res_stack, char e)
{
// 计算的时候是用 下边的数 运算符 上边的数
double b = *(--res_stack->top);
double a = *(--res_stack->top);
switch (e)
{
case '+': *(res_stack->top) = a+b; break;
case '-': *(res_stack->top) = a-b; break;
case '*': *(res_stack->top) = a*b; break;
case '/': *(res_stack->top) = a/b; break;
}
// 压栈完毕之后, top指针需要往上走一步
res_stack->top ++;
}
// 把数字压入栈中
void res_stack_push(resStack *res_stack, char e, char previous)
{
if (res_stack->top == res_stack->base)
{
*(res_stack->top) = e - 48;
res_stack->top ++;
return;
}
if (previous >= 48 && previous <= 57) // 上一个如果是个数,则进行拼接
{
double sum = *(--res_stack->top) * 10 + (e - 48);
*(res_stack->top) = sum;
res_stack->top ++;
}
else
{
// 如果上一个不是数字则直接压栈
*(res_stack->top) = (e - 48);
res_stack->top ++;
}
}
// 拼接小数点后边的数字
void point_cnn(resStack *res_stack, char e, int cnt)
{
double n = e - 48;
double cnnt = *(--res_stack->top) + n/pow(10, cnt);
*(res_stack->top) = cnnt;
res_stack->top ++;
}
// 把符号栈内剩余的符号弹出, 进行计算
void clearSymbolStack(sqStack *symbol_stack, resStack *res_stack)
{
do
{
symbol_stack->top --;
resStackCalc(res_stack, *(symbol_stack->top));
} while (symbol_stack->top != symbol_stack->base);
}
// 销毁栈
void destroyStack(sqStack *symbol_stack, resStack *res_stack)
{
free(symbol_stack->base);
free(res_stack->base);
// 让指针指向NULL, 防止野指针出现
symbol_stack->base = symbol_stack->top = NULL;
res_stack->base = res_stack->top = NULL;
symbol_stack->stack_size = res_stack->stack_size = 0;
}
int main(void)
{
char input;
sqStack symbol_stack;
resStack res_stack;
initResStack(&res_stack);
initSqStack(&symbol_stack);
printf("输入中缀表达式:");
do
{
scanf("%c", &input);
if (input != '#')
{
divideStack(&symbol_stack, &res_stack, input);
}
} while (input != '#');
getchar(); // 清空缓冲区
clearSymbolStack(&symbol_stack, &res_stack);
printf(">> %f", *(res_stack.base)); // 此时数字栈栈底就是结果
destroyStack(&symbol_stack, &res_stack);
return 0;
}
注:输入表达式时候不要有空格,表达式以符号 '#' 结尾!
运行结果:
输入中缀表达式:1*(9/2-1+0.3-8/0.2)+0.002#
>> -36.198000
输入中缀表达式:0.002*0.1+3*((9-6)/2-0.5)#
>> 3.000200