项目名称:计算器
方案论证:
1、检查表达式是否合法:我们只需要采取栈的平衡算法+使用many many many 的if就可以实现啦
//检查表达式是否合法
//在这里我们认为'.'前若没有数字的表达式不合法
// 在这里我们认为'.'后若没有数字的表达式不合法
// 在这里我们认为()不合法
// 我们认为0X是正确的表达方式
//1.检查括号是否平衡
bool check1(string str)
{
stack<char>stk1;
for (int i = 0; i < str.length(); i++)
{
if (str[i] == '(')
stk1.push(str[i]);
else if (str[i] == ')')
{
if (stk1.empty() || stk1.top() != '(')
{
return false;
}
stk1.pop();
}
}
if (!stk1.empty())
return false;
else
return true;
}
//2.检查运算符之间连接问题和运算符与数字之间的连接问题+-*/().^
bool check2(string str)
{
if (str[0] == '+' || str[0] == '-' || str[0] == '*' || str[0] == '/' || str[0] == ')' || str[0] == '^' || str[0] == '.')
return false;
for (int i = 1; i < str.length(); i++)
{
if (isdigit(str[i]))//数字前面绝对不能有')',后面绝对不能有'('
{
if (str[i-1]=='/' && str[i] == '0' && str[i + 1] != '.')//因为可以进行小数运算,所以我们不能一棍子打死说0前面有/都是错的
{
return false;
}
else
{
if (str[i - 1] == ')')
return false;
if (str[i + 1] == '(')
return false;
}
}
else if (str[i] == '+' || str[i] == '-' || str[i] == '*' || str[i] == '/')
{
if ((str[i] == '+' || str[i] == '*' || str[i] == '/') && (str[i - 1] == '('))//负数前面可以有(
return false;
if (str[i - 1] == '+' || str[i - 1] == '-' || str[i - 1] == '*' || str[i - 1] == '/' || str[i - 1] == '.' || str[i - 1] == '^')//想用负数幂肯定得使用()
return false;
if (!isdigit(str[i+1]) && str[i+1]!='(')
return false;
}
else if (str[i] == '(' || str[i] == ')')//'('和')'的部分不合法情况在之前判断过了
{
if (str[i - 1] == '.')
return false;
if ((str[i] == '(') && (str[i + 1] == '.' || str[i + 1] == '^'))
return false;
if ((str[i] == ')') && (str[i + 1] == '.' || str[i-1] == '('))
return false;
}
else if (str[i] == '.')//'.'的部分不合法情况在之前判断过了
{
if (str[i - 1] == '.' || str[i-1] == '^')
return false;
if (str[i + 1] == '.' || str[i+1] == '^')
return false;
}
else if (str[i] == '^')
{
if (str[i - 1] == '^')
return false;
}
}
}
2、为了实现负数运算,我们需要将表达式的和式一一用@@包住----be like ((5+6*4)+5) --》((@5@+@6@*@4@)+@5@)
//将所传入的字符串中数字用@分隔
string transformer(string& s)
{
if (isdigit(s[0]))//先单独考虑第一个字符是否为数字
s.insert(0, 1, '@');
if (isdigit(s[s.length() - 1]))//如果最后一个字符是数字,那么就要加上@
s += '@';
for (int i = 1; i < s.length()-1; i++)
{
//给数字前面加上@
if (s[i] == '-' && s[i - 1] == '(')
s.insert(i, 1, '@');
else if (isdigit(s[i]) && (s[i - 1] == '+' || s[i - 1] == '*' || s[i - 1] == '/' || s[i - 1] == '^' || s[i-1] == '('))
s.insert(i, 1, '@');
else if (isdigit(s[i]) && s[i - 1] == '-')
{
if (i >= 2 && s[i - 3] != '(')//i-3的原因是如果是负数的话前面已经加过一个@了
s.insert(i, 1, '@');
}
//给数字后面加上@
if (isdigit(s[i]) && (s[i + 1] == '+' || s[i + 1] == '-' || s[i + 1] == '*' || s[i + 1] == '/' || s[i + 1] == '^' || s[i + 1] == ')'))
s.insert(i + 1, 1, '@');
}
return s;
}
3、中缀表达式转后缀表达式
1、准备工作,定义运算符优先级
//规定运算符优先级
int priority(char c)
{
if (c == '+' || c == '-')
return 0;
else if (c == '*' || c == '/')
return 1;
else if (c == '^')
return 2;
else if (c == '(' || c == ')')
return 3;
}
2、中缀转后缀的思路为:依次读入,若字符是数字,则输出,若字符为运算符,且栈顶元素优先级比它低或栈顶元素是左括号,则该字符压入栈中,若栈顶元素优先级比它高或相等且不是左括号,则将栈顶元素弹出并且输出,最后将该运算符输出并弹出,最后读入为空时将符号全部弹出并输出。
//中缀转后缀
string transition(string str)
{
int num = 0;
stack<char>stk;//专门用于存储运算符
string output = "";
int flag = 0;//这个flag特别重要!!!因为在碰到'('后,由于'('优先级最高,我们必须通过flag来抵消掉'('的最高优先级,
//如果出现过'(',就要把flag变为1,然后在'('后面的运算符入栈之后,将flag变为0,然后后面就可以正常比较运算符优先级了。
for (int i = 0; i < str.length(); i++)
{
if (str[i] == '@')
output += str[i];
else if (str[i] == '.')
output += '.';
else if (isdigit(str[i]))
output += str[i];
else if (str[i] == '-' && str[i - 1] == '@' && (str[i - 2] == '@' || str[i - 2] == '('))//如果发现减号前面有个@,则将减号放入输出中
output += '-';
else if(str[i]!='('&&str[i]!=')')
{
if (stk.empty())
stk.push(str[i]);
else if (flag == 1)
{
num--;
stk.push(str[i]);
flag = 0;
}
else if (priority(str[i]) > priority(stk.top()) || str[i]=='^' )//由于幂运算从右向左
stk.push(str[i]);
else if ((priority(str[i]) <= priority(stk.top())) && (str[i]!='^'))//由于幂运算从右向左
{
output += stk.top();
stk.pop();
stk.push(str[i]);
}
}
else//由于已经确保表达式合法,所以'(' ')'成对出现
{
if (str[i] == '(')
{
flag = 1;
num++;
stk.push(str[i]);
}
else if(str[i] == ')')
{
if (num > 0) {
flag = 1;
}
while (stk.top() != '(')
{
output += stk.top();
stk.pop();
}
stk.pop();//让'('出栈
}
}
}
while (!stk.empty())
{
output += stk.top();
stk.pop();
}
return output;
}
4、后缀+栈计算结果
对于后缀表达式,我们只需依次读取,读到数字则压入栈中,读到运算符则将栈顶两个元素弹出,进行相应运算即可。
//后缀+栈计算结果
double getResult(string s)
{
stack<double>result;
int j = 0;
int num = 0;//num是用来记录@的个数的
for (int i = 0; i < s.length(); i=j)
{
if (s[i]=='@')
{
if (num % 2 == 0)
{
num++;
int pos = i;
j = pos + 1;
for (; j < s.length(); j++)
{
if (s[j] == '@')
{
result.push(stod(s.substr(pos + 1, j - pos - 1)));
break;
}
}
}
else
{
num++;
j++;
}
}
else if (s[i] == '+')
{
double temp1 = result.top();
result.pop();
double temp2 = result.top();
result.pop();
result.push(temp1 + temp2);
j++;
}
else if (s[i] == '-')
{
double temp1 = result.top();
result.pop();
double temp2 = result.top();
result.pop();
result.push(temp2 - temp1);
j++;
}
else if (s[i] == '*')
{
double temp1 = result.top();
result.pop();
double temp2 = result.top();
result.pop();
result.push(temp2 * temp1);
j++;
}
else if (s[i] == '/')
{
double temp1 = result.top();
result.pop();
double temp2 = result.top();
result.pop();
result.push(temp2 / temp1);
j++;
}
else if (s[i] == '^')
{
double temp1 = result.top();
result.pop();
double temp2 = result.top();
result.pop();
result.push(pow(temp2 , temp1));
j++;
}
}
return result.top();
}
5、main函数
int main()
{
string str;
while (cin >> str)
{
if (check1(str) && check2(str))
{
cout << getResult(transition(transformer(str))) << endl;
}
else
cout << "the input expression syntax is incorrect !" << endl;
}
return 0;
}