C++用栈实现计算器(正负小数+-*/^运算,表达式合法检测)

项目名称:计算器

方案论证:

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;
}

  • 2
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值