思路:
两个栈:数据栈与符号栈。
(在描述优先级时,我们将加减描述为‘I级’,乘除为‘II既’,乘方为‘III级’)
1.在之前的只有加减乘除的计算表达式里:就有了初步思路
当我们要计算的时候:
局部计算函数:数字栈与符号栈,分别在数字栈顶取出两个操作数,再在符号栈里取出对应的符号进行运算。
在只有加减乘除的计算中:我们只需要考虑两种情况来调用局部计算函数,
一:当存入一个数字压栈时,判断出符号栈顶是优先级的'*'或'/',II级时,直接进入计算函数。
二:当读到右括号时,进行while的计算函数操作,其目的就是把括号里低级,I级的加减进行计算。
加入乘方后的难点:比如: 2*3^4,如果按上述会产生读到3后判断出前一个为'*',就进行计算函数的操作,而乘方^ 优先级应高于*,导致错误。
解决方法:前文解决乘除的优先级问题时,是通过判断压入数字判符号栈顶元素是否为II级即可。同理,我们可以在判断数字时,顺便判断它后方是否为III级,如果是,则再获取III后方的数字,并且直接通过乘方计算后再压入数字栈;比如: a*b^c,当读入b后,判断出后一位为III,则再往后读数字c,然后压栈时压入的就是(b^c),就转化为了只有加减乘除的问题。顺便判断它后方是否为III级,如果是,但后边有括号如:a^(b+c)
故以下我们主要讨论:
1.a*b^c2.(a+b)^c与a^(b+c)
3.a*b^(c+d)与b^(c+d)*a
我的测试:
a+b a+b+c+d a*b a*b*c*d a^b
a^b^c^d a*b^c b^c*a (a+b)^c a^(b+c) a*b^(c+d) b^(c+d)*a
(方法较为复杂,只是以个人角度解题,在使用不同表达式时修修改改,该程序可能依然有隐藏bug!!!)
对于 a*b^c 可以使用上述解决方法,当读入数字是判断后方是否为III并且将III后方数字也同样计算出来再压回栈里:
对于:a^(b+c)则需要避免这种情况:故应该有:&& str[j+1]!='('
if (isNumber(temp)) {
int j = i;
int sum = 0, power = 1;
while (isNumber(str[j]) && j < strlen(str)) {
sum = sum * 10 + str[j] - '0';
j++;
}
if ( str[j] == '^' && j<strlen(str)
&& head->dataC[head->topC] != '^'
&& str[j+1]!='(' && str[j + 1] != ')' )
{
j++;//2^(2+3)
while (isNumber(str[j]) && j < strlen(str)) {
power = power * 10 + str[j] - '0';
j++;
}
sum = pow(sum, power);
}
i = j - 1;
pushI(head, sum);
对于 (a+b)^c:则可以采用乘除的判断方法: 压入了一个数字并且发现此时符号栈顶为'^',则直接计算:
if (head->dataC[head->topC] == '*' || head->dataC[head->topC] == '/') cal_loc(head);
if(head->dataC[head->topC] == '^') cal_loc(head);
对于a^(b+c) :通过计算完成括号内数字后,若栈顶为III,则再进行计算a*b^(c+d)与b^(c+d)*a:if (head->dataC[head->topC] == '*' &&str[i+1]!='^') cal_loc(head);这样是为了确保 ^ 始终都先被计算;
case')':
while(head->dataC[head->topC]!='(') cal_loc(head);
popC(head);
if(head->dataC[head->topC] == '^') cal_loc(head);
if (head->dataC[head->topC] == '*' &&str[i+1]!='^') cal_loc(head);
break;
局部计算函数:
void cal_loc(stackPtr head) {
if (head->dataC[head->topC] == '(') return;
int a = popI(head);
int b = popI(head);
char c = popC(head);
int res = 0;
switch (c) {
case'^':
res = pow(b, a);
break;
case'*':
res = a * b;
break;
case'/':
res = (double)b / a;
break;
case'+':
res = a + b;
break;
case'-':
res = b - a;
break;
}
pushI(head, res);
}
逻辑判断主函数:
int calculate(stackPtr head,char* str) {
for (int i = 0; i <= strlen(str);i++ ) {
PrintC(head);
putchar('\n');
PrintI(head);
putchar('\n');
char temp;
if (i < strlen(str)) {
temp = str[i];
}
else {
temp = ')';
}
if (isNumber(temp)) {
int j = i;
int sum = 0, power = 0;
while (isNumber(str[j]) && j < strlen(str)) {
sum = sum * 10 + str[j] - '0';
j++;
printf("sum1:%d \n", sum);
}
if (str[j] == '^' && j<strlen(str) && head->dataC[head->topC] != '^' && str[j+1]!='(' && str[j + 1] != ')') {
j++;//2^(2+3)
while (isNumber(str[j]) && j < strlen(str)) {
power = power * 10 + str[j] - '0';
j++;
}
sum = pow(sum, power);
printf("sum2:%d \n", sum);
}
i = j - 1;
pushI(head, sum);
printf("pushed:");
PrintI_TOP(head);
if ((head->dataC[head->topC] == '*' || head->dataC[head->topC] == '/') && str[i+1]!='^') cal_loc(head);
if(head->dataC[head->topC] == '^') cal_loc(head);
}
else {
switch (temp) {
case'(':
pushC(head, temp);
break;
case')':
while(head->dataC[head->topC]!='(') cal_loc(head);
popC(head);
if (head->dataC[head->topC] == '^') cal_loc(head);
if (head->dataC[head->topC] == '*' && str[i+1]!='^') cal_loc(head);
break;
case '^':
pushC(head, temp);
break;
case '*':
pushC(head, temp);
break;
case '/':
pushC(head, temp);
break;
case'+':
pushC(head, temp);
break;
case'-':
pushC(head, temp);
break;
}
}
}
return popI(head);
}
完整代码:
#include<stdio.h>
#include<malloc.h>
#include<string.h>
#include<math.h>
typedef struct {
char dataC[20];
int dataI[20];
int topC,topI;
}*stackPtr, stack;
stackPtr InitStack() {
stackPtr head = (stackPtr)malloc(sizeof(stack));
head->topC = -1;
head->topI = -1;
return head;
}
void pushC(stackPtr head, char c) {
if (head->topC >= 18) {
printf("no space");
return;
}
head->topC++;
head->dataC[head->topC] = c;
}
char popC(stackPtr head) {
char tempchar = head->dataC[head->topC];
head->topC--;
return tempchar;
}
void pushI(stackPtr head, int c) {
if (head->topI >= 18) {
printf("no space");
return;
}
head->topI++;
head->dataI[head->topI] = c;
}
int popI(stackPtr head) {
int tempint = head->dataI[head->topI];
head->topI--;
return tempint;
}
int isNumber(char c) {
if (c >= '0' && c <= '9') return 1;
return 0;
}
void PrintC(stackPtr head) {
if (head->topC == -1) return;
for (int i = head->topC; i >= 0; i--) {
printf("%c ", head->dataC[i]);
}
}
void PrintI(stackPtr head) {
if (head->topI == -1) return;
for (int i = head->topI; i >= 0; i--) {
printf("%d ", head->dataI[i]);
}
}
void PrintI_TOP(stackPtr head) {
if (head->topI == -1) return;
printf("%d\n", head->dataI[head->topI]);
}
void cal_loc(stackPtr head) {
if (head->dataC[head->topC] == '(') return;
int a = popI(head);
int b = popI(head);
char c = popC(head);
int res = 0;
switch (c) {
case'^':
res = pow(b, a);
break;
case'*':
res = a * b;
break;
case'/':
res = (double)b / a;
break;
case'+':
res = a + b;
break;
case'-':
res = b - a;
break;
}
pushI(head, res);
}
int calculate(stackPtr head,char* str) {
for (int i = 0; i <= strlen(str);i++ ) {
PrintC(head);
putchar('\n');
PrintI(head);
putchar('\n');
char temp;
if (i < strlen(str)) {
temp = str[i];
}
else {
temp = ')';
}
if (isNumber(temp)) {
int j = i;
int sum = 0, power = 0;
while (isNumber(str[j]) && j < strlen(str)) {
sum = sum * 10 + str[j] - '0';
j++;
printf("sum1:%d \n", sum);
}
if (str[j] == '^' && j<strlen(str) && head->dataC[head->topC] != '^' && str[j+1]!='(' && str[j + 1] != ')') {
j++;//2^(2+3)
while (isNumber(str[j]) && j < strlen(str)) {
power = power * 10 + str[j] - '0';
j++;
}
sum = pow(sum, power);
printf("sum2:%d \n", sum);
}
i = j - 1;
pushI(head, sum);
printf("pushed:");
PrintI_TOP(head);
if ((head->dataC[head->topC] == '*' || head->dataC[head->topC] == '/') && str[i+1]!='^') cal_loc(head);
if(head->dataC[head->topC] == '^') cal_loc(head);
}
else {
switch (temp) {
case'(':
pushC(head, temp);
break;
case')':
while(head->dataC[head->topC]!='(') cal_loc(head);
popC(head);
if (head->dataC[head->topC] == '^') cal_loc(head);
if (head->dataC[head->topC] == '*' && str[i+1]!='^') cal_loc(head);
break;
case '^':
pushC(head, temp);
break;
case '*':
pushC(head, temp);
break;
case '/':
pushC(head, temp);
break;
case'+':
pushC(head, temp);
break;
case'-':
pushC(head, temp);
break;
}
}
}
return popI(head);
}
void Test() {
stackPtr head = InitStack(head);
pushC(head, '(');
char* tempExpression = "10+(3+2)^4";
printf("The result = %d ", calculate(head, tempExpression));
}
int main() {
Test();
return 0;
}
ps:提前压入‘(’和末尾压‘)’的原因是:涉及加减的运算在该程序需要读到‘)’才会进行计算,但可能表达式中没有,故人为加入‘)’。