分析:由于要求用C语言,我们用一个char类型的数组存表达式字符串,用一个int类型的二维数组存左括号和右括号的位置,然后直接对该表达式字符串按优先级进行处理,核心功能为:
(1)对括号的深度遍历,并存入flag二维数组中的findKuohao();子函数。
计算时优先处理最后面的括号,即表达式中最后面那组括号中最里面的那层。依次向前计算。
(2)字符串中某段表达式求解后的值再返回字符串时,对该段后面字符串的移动处理moveStr(int left, int right, int numLen);子函数。
(3)flag二维数组中括号的位置 跟随 字符串的移动 而变化的flagFolMoveStr();子函数。
PS:由于自己临时编写,算法的复杂度可能并不最优,欢迎大家交流优化,提一些意见,龙少感激不尽,代码如下:
创建一个priority.h头文件,代码如下:
#ifndef _PRIORITY_H_
#define _PRIORITY_H_
//求运算符优先级
int priority(double o)
{
switch((int)o)
{
case 0: return 0;
case 5: return 1;
case 1:
case 2: return 2;
case 3:
case 4: return 3;
}
}
//判断是否为运算符
int isOperator(char c)
{
if(c == '+' || c == '-' || c == '*' || c == '/' || c == '(' || c == ')') return 1;
else return 0;
}
//判断是否为数字
int isNum(char c)
{
if(c >= '0' && c <= '9') return 1;
else return 0;
}
#endif
创建一个main.c文件,代码如下:
/*
* 重点难点:
* 1. 负数的运算
* 2. 括号的嵌套
* 2.1 括号的多层嵌套
* 2.2 同层中多个括号
*/
#include <stdio.h>
#include <string.h>
#include "priority.h"
#include <stdlib.h>
char s[512]; //总
int iS = 0; //s的下标
int flag[128][2]; //存括号的位置flag[iFlag][0]为第iFlag个括号的左括号,flag[iFlag][1]是右括号
int iFlag = 0; //flag的顶部
int flagLength = -1; //括号的个数,即flag的长度
double res; //最后的结果
double x, y; //x 'o' y
char o;
int leftKH, rightKH; //当前运算的左右括号
void doCC();
void doJJ();
void flagFolMoveStr();
void moveStr(int left, int right, int numLen);
void doNum(double *num);
void findKuohao();
//括号的位置跟随整体字符串的移动而改变
void flagFolMoveStr()
{
int i = flagLength;
int len1 = rightKH - leftKH; //现在左右括号之间的长度
int len2 = flag[i][1] - flag[i][0]; //原来左右括号之间的长度
int len = len2 - len1; //变化的长度,即字符串移动的距离
if(len != 0)
while(i >= 0) //将存括号的数组内容,跟随当前移动后面字符串的操作,同步更改括号位置
{
if(flag[i][1] > flag[flagLength][1]) //如果第i个右括号位置在当前右括号的后面,则更新该括号当前位置
flag[i][1] -= len; //每一个括号的位置都前移len
i--;
}
flag[flagLength][0] = 0, flag[flagLength][1] = 0; //当前括号位置清零
flagLength--; //括号个数减一
}
//将字符串s[left...right]内容替换为num,要移动后面的内容
void moveStr(int left, int right, int numLen)
{
int i = right;
int len = right - left;
int moveLen = left + numLen - right;
if(len == numLen) return; //如果删除的长度和要插入的数字长度相等,无需移动后面元素
else if(len > numLen) //如果大于,后面元素前移
{
while(s[i-1])
{
s[i+moveLen] = s[i];
i++;
}
memset(s+i+moveLen, 0, -moveLen); //前移之后,将后面使用过的空间清零
}
else //否则小于,后面元素后移
{
while(s[i+1]) i++; //i指向最后一位
while(i >= right) //后面元素后移
{
s[i+moveLen] = s[i];
i--;
}
}
rightKH += moveLen;
}
//提取数字给num
void doNum(double *num)
{
char numTemp[32]; //字符串转double的临时字符串数组
int i; //下标,索引,遍历介质
memset(numTemp, 0, sizeof(numTemp));
i = 0;
if(s[iS] == '-') numTemp[i++] = s[iS++];
else if(!isNum(s[iS]))
{
puts("表达式有误!");
exit(EOF);
}
while(isNum(s[iS]))
{
numTemp[i] = s[iS];
i++, iS++;
}
if(s[iS] == '.')
{
numTemp[i++] = '.';
iS++;
while(isNum(s[iS])) //小数部分
{
numTemp[i] = s[iS];
i++, iS++;
}
}
*num = atof(numTemp);
}
//查找s中括号的位置,存入flag中
void findKuohao()
{
if(s[iS] == '(') //如果遇到左括号,flag[iFlag][0]存左括号的位置,1存右括号的位置
{
flag[iFlag][0] = iS; //存左括号的位置
iS++;
while(s[iS] && s[iS] != ')') //找右括号
{
if(s[iS] == '(') //如果又遇到左括号,递归调用该函数,将存到flag的下一个位置
{
while(flag[iFlag][0] != -1)
{
iFlag++;
}
findKuohao();
iFlag--;
while(flag[iFlag][1] != -1)
iFlag--; //寻找完毕,iFlag回来继续找该层的右括号
}
iS++;
}
if(!s[iS]) //如果字符串到尾部还没找到右括号,匹配失败
{
printf("表达式中括号不匹配!\n");
exit(EOF);
}
flag[iFlag][1] = iS; //存入右括号
flagLength++;
while(flag[iFlag][1] != -1)
iFlag++;
}
}
//做乘除运算
void doCC()
{
double res;
char resTemp[16], *pr;
int liftTemp, rightTemp;
while(iS < rightKH)
{
if(s[iS] == '*' || s[iS] == '/')
{
pr = resTemp;
memset(resTemp, 0, sizeof(resTemp));
//将指针前移至运算符前的数字首地址
iS--;
while(isNum(s[iS]) || s[iS] == '.' || s[iS] == '-' && !isNum(s[iS-1]) ) iS--;
iS++;
//记录表达式左端
liftTemp = iS;
//将运算符左面数字的字符串转为double赋值给x,同时指针指向数字的下一位
doNum(&x);
//数字的下一位即运算符,存在o中
o = s[iS];
//将指针指向运算符后面数字的首地址
iS++;
//将运算符右面数字的字符串转为double赋值给y,同时指针指向数字的下一位
doNum(&y);
//记录表达式的右端的下一位
rightTemp = iS;
switch(o)
{
case '*': res = x * y; break;
case '/': res = x / y; break;
}
sprintf(resTemp, "%lf", res);
moveStr(liftTemp, rightTemp,strlen(resTemp));
while(*pr) s[liftTemp++] = *(pr++);
// printf("doCC: %s——%s\n", resTemp, s);
iS = liftTemp - 1;
}
iS++;
}
}
//做加减运算
void doJJ()
{
double res;
char resTemp[16], *pr;
int leftTemp, rightTemp;
while(iS < rightKH)
{
if(iS - 1 >= 0 && (s[iS] == '+' || s[iS] == '-'))
{
memset(resTemp, 0, sizeof(resTemp));
pr = resTemp;
iS--;
while(isNum(s[iS]) || s[iS] == '.' || s[iS] == '-' && !isNum(s[iS-1])) iS--;
iS++;
leftTemp = iS;
doNum(&x);
o = s[iS];
iS++;
doNum(&y);
rightTemp = iS;
switch(o)
{
case '+': res = x + y; break;
case '-': res = x - y; break;
}
sprintf(resTemp, "%lf", res);
moveStr(leftTemp, rightTemp, strlen(resTemp));
while(*pr) s[leftTemp++] = *(pr++);
// printf("doJJ: %s——%s\n", resTemp, s);
iS = leftTemp - 1;
}
iS++;
}
}
int main()
{
memset(flag, -1, sizeof(flag));
memset(s, 0, sizeof(s));
gets(s);
//找括号的位置
while(s[iS])
{
findKuohao();
iS++;
}
int i;
//先计算括号里的值
while(flagLength > -1)
{
// for(i = 0; i <= flagLength; i++) printf("%d,, %d\n", flag[i][0], flag[i][1]);//(3-2+(6-5)/(5-4)/(4-2-1))
//1 获取最后的最内层的括号的位置
leftKH = flag[flagLength][0];
rightKH = flag[flagLength][1];
iS = leftKH;
//2 从左括号开始计算
//2.1 优先计算乘除
doCC();
iS = leftKH;
//2.2 再计算加减
doJJ();
moveStr(leftKH, leftKH + 1, 0); //删除左括号
moveStr(rightKH, rightKH + 1, 0); //删除右括号
flagFolMoveStr();
}
//没有括号了,正常运算
iS = 0, rightKH = strlen(s);
//1 优先计算乘除
doCC();
iS = 0, rightKH = strlen(s);
//2 再计算加减
doJJ();
printf("= %s\n", s);
system("pause");
return 0;
}