括号匹配,实现简单计算器(加减乘除,小括号)

括号匹配算法

利用栈先进后出的特性,思路分三步:
1.遇到左括号,直接入栈,继续向后遍历;
2.遇到右括号,
(1).如果栈为空,说明无对应左括号,则返回表达式不匹配;
(2).如果栈不为为空,将栈顶元素弹出判断是否是相匹配的括号;
如果匹配,将栈顶元素弹出,继续遍历;如果不匹配,说明不是同类括号,直接返回表达式不匹配。
3.遍历到表达式末尾,如果栈中有剩余元素,说明存在多余左括号,返回表达式不匹配;如果栈为空,返回表达式匹配。
测试样例:

	//(56-20)/(4+2)
	//9 * (10 + 11 * (12 + 13)) + 14
	//(15 + 16) * (17 + 18 * (19 + 20)
	//((21 + 22) * 23 + (24 * 25)) + 26 * 27)) + 28	
	//{asdsd{ddads}sa}[]sassada[]sda{
    
    {aassaddsd[]}dsad}

完整程序实现如下:

#include<bits/stdc++.h>
using namespace std;


bool judge(char str[]){
    
    
	
	stack<char> s;
	int n = strlen(str);
	for(int i = 0; i < n; i++){
    
    
		if(str[i]=='(') s.push(str[i]);//左括号入栈 
		if(str[i]=='[') s.push(str[i]);
		if(str[i]=='{') s.push(str[i]);
		if(str[i]==')'){
    
    
			if(s.empty()) return false;//无左括号不匹配 
			else if(s.top()=='(') s.pop();//匹配 
			else return false;//不是同类括号 
		}
		if(str[i]==']'){
    
    
			if(s.empty()) return false;
			else if(s.top()=='[') s.pop();
			else return false;
		}
		if(str[i]=='}'){
    
    
			if(s.empty()) return false;
			else if(s.top()=='{') s.pop();
			else return false;
		}
	}
	return s.empty();//空匹配 非空说明存在多余左括号 
}
int main(){
    
    
	
	char str[10000];
	cin>>str; 
	//(56-20)/(4+2)
	//9 * (10 + 11 * (12 + 13)) + 14
	//(15 + 16) * (17 + 18 * (19 + 20)
	//((21 + 22) * 23 + (24 * 25)) + 26 * 27)) + 28	
	//{asdsd{ddads}sa}[]sassada[]sda{
    
    {aassaddsd[]}dsad}
	bool v = judge(str);
	if(v) cout<<"匹配"<<endl;
	else cout<<"不匹配"<<endl;
	return 0;
}

简单计算器实现:设计一个计算器,实现加减乘除和小括号的计算。

设计思路:构建两个栈,一个压入要进行计算的数字,一个压入运算符,运算符涉及到优先级问题,括号>乘除>加减。遍历要计算的字符串,数字压入栈num,运算符压入栈s:
1.如果栈s为空,直接将运算符压入栈s;
2.如果栈s不为空,且栈顶运算符优先级大于等于待入栈运算符,将栈顶运算符弹出,从栈num弹出两个数字,进行运算,将计算结果压入栈num,直至栈空或栈顶运算符优先级小于待入栈运算符,将待入栈元素入栈;
3.如果栈s不为空,且栈顶运算符优先级小于待入栈运算符,直接将待入栈运算符入栈;
4.遇到左括号直接入栈,重复2,3步骤,遇到右括号时,若栈s栈顶运算符为左括号,直接将左括号弹出;若不为左括号,将运算符依次弹出进行运算,直至栈顶运算符为左括号,将其弹出。
5.字符串遍历完成后,若栈s为空,栈num栈顶元素即运算结果;栈s不为空,将运算符依次弹出进行计算。

涉及程序时,我们先写一个函数prio来返回运算符的优先级,加减返回1,乘除返回2,小括号返回3和4。

int prio(char ch){
    
    
	
	switch(ch){
    
    
	    case '+':
	    case '-':
	        return 1;
	    case '*':
	    case '/':
	        return 2;
	    case'(':
	        return 3;
	    case ')':
	        return 4;
    }
}

再写一个运算函数:

int cal(int a,int b,char ch){
    
    
	
	switch(ch){
    
    
	    case '+':
	    	return a+b;
	    case '-':
	        return a-b;
	    case '*':
	    	return a*b;
	    case '/':
	        return a/b;
    }
}

上述5步我们慢慢拆分:
首先是遍历字符串:

for(int i=0;i<n;i++)

数字入栈:

if('0'<=str[i]&&str[i]<='9'){
    
    
	x = x*10+(str[i]-'0'); 
	v=1;
}
if(str[i]>'9'||str[i]<'0'||i==n-1){
    
    
	if(v==1){
    
    
		num.push(x);				
		//cout<<"push:"<<x<<endl;
		x=0;
	}
	v=0;
}

加v标记的原因是如果多个运算符相连,会导致压入不必要的数字0。
运算符入栈:

if(str[i]=='+'||str[i]=='-'||str[i]=='*'||str[i]=='/'||str[i]=='('||str[i]==')'){
    
    

1.如果栈s为空,直接将运算符压入栈s;

if(s.empty()) s.push(str[i]);

2.如果栈s不为空,且栈顶运算符优先级大于等于待入栈运算符,将栈顶运算符弹出,从栈num弹出两个数字,进行运算,将计算结果压入栈num,直至栈空或栈顶运算符优先级小于待入栈运算符,将待入栈元素入栈;

while(!s.empty()&&prio(s.top())>=prio(str[i])&&s.top()!='('){
    
    
	int x2 = num.top();
	num.pop();
	int x1 = num.top();
	num.pop();
	char ch = s.top();
	s.pop();
	//cout<<x1<<ch<<x2<<" "<<cal(x1,x2,ch)<<endl;
	num.push(cal(x1,x2,ch));
}

3.如果栈s不为空,且栈顶运算符优先级小于待入栈运算符,直接将待入栈运算符入栈;

s.push(str[i]);

4.遇到左括号直接入栈,重复2,3步骤,遇到右括号时,若栈s栈顶运算符为左括号,直接将左括号弹出;若不为左括号,将运算符依次弹出进行运算,直至栈顶运算符为左括号,将其弹出。

if(str[i]=='('){
    
    
	s.push(str[i]);
	continue;
}
if(str[i]==')'){
    
    
	while(!s.empty()&&s.top()!='('){
    
    
		int x2 = num.top();
		num.pop();
		int x1 = num.top();
		num.pop();
		char ch = s.top();
		s.pop();
		//cout<<x1<<ch<<x2<<" "<<cal(x1,x2,ch)<<endl;
		num.push(cal(x1,x2,ch));
	}
	s.pop();
	continue;
}

5.字符串遍历完成后,若栈s为空,栈num栈顶元素即运算结果;栈s不为空,将运算符依次弹出进行计算。

while(!s.empty()){
    
    
	int x2 = num.top();
	num.pop();
	int x1 = num.top();
	num.pop();
	char ch = s.top();
	s.pop();
	//cout<<x1<<ch<<x2<<" "<<cal(x1,x2,ch)<<endl;
	num.push(cal(x1,x2,ch));
}
return num.top();

完整程序实现如下:

#include<bits/stdc++.h>
using namespace std;

int prio(char ch){
    
    
	
	switch(ch){
    
    
	    case '+':
	    case '-':
	        return 1;
	    case '*':
	    case '/':
	        return 2;
	    case'(':
	        return 3;
	    case ')':
	        return 4;
    }
}

int cal(int a,int b,char ch){
    
    
	
	switch(ch){
    
    
	    case '+':
	    	return a+b;
	    case '-':
	        return a-b;
	    case '*':
	    	return a*b;
	    case '/':
	        return a/b;
    }
}

int calculation(char str[]){
    
    
	stack<char> s;
	stack<int> num;
	int n = strlen(str);
	int x = 0;//用于提出字符串数字 
	int v = 0;//用于判断何时将数字压入栈 
	for(int i=0;i<n;i++){
    
    
		
		//cout<<str[i]<<endl;
		if('0'<=str[i]&&str[i]<='9'){
    
    
			x = x*10+(str[i]-'0'); 
			v=1;
		}
		if(str[i]>'9'||str[i]<'0'||i==n-1){
    
    
			if(v==1){
    
    
				num.push(x);				
				//cout<<"push:"<<x<<endl;
				x=0;
			}
			v=0;
		}
		if(str[i]=='+'||str[i]=='-'||str[i]=='*'||str[i]=='/'||str[i]=='('||str[i]==')'){
    
    
			if(str[i]=='('){
    
    
				s.push(str[i]);
				continue;
			}
			if(str[i]==')'){
    
    
				while(!s.empty()&&s.top()!='('){
    
    
					int x2 = num.top();
					num.pop();
					int x1 = num.top();
					num.pop();
					char ch = s.top();
					s.pop();
					//cout<<x1<<ch<<x2<<" "<<cal(x1,x2,ch)<<endl;
					num.push(cal(x1,x2,ch));
				}
				s.pop();
				continue;
			}
			if(s.empty()) s.push(str[i]);
			else {
    
    
				//cout<<s.top()<<" "<<str[i]<<endl;
				while(!s.empty()&&prio(s.top())>=prio(str[i])&&s.top()!='('){
    
    
					int x2 = num.top();
					num.pop();
					int x1 = num.top();
					num.pop();
					char ch = s.top();
					s.pop();
					//cout<<x1<<ch<<x2<<" "<<cal(x1,x2,ch)<<endl;
					num.push(cal(x1,x2,ch));
				}
				s.push(str[i]);
			}
		}
	}
	while(!s.empty()){
    
    
		int x2 = num.top();
		num.pop();
		int x1 = num.top();
		num.pop();
		char ch = s.top();
		s.pop();
		//cout<<x1<<ch<<x2<<" "<<cal(x1,x2,ch)<<endl;
		num.push(cal(x1,x2,ch));
	}
	return num.top();
}

int main(){
    
    
	
	char str[10000];
	cin>>str; 
	//(56-20)/(4+2)
	//9*(10+11*(12+13))+14
	//(15+16)*(17+18*(19+20))
	//(((21+22)*23+(24*25))+26*27)+28	
	//10+11*12/12+14-1+2*3*1-5/5*5-5-5-5*5/5*5/5
	//10+11-12/12+14-1+22/2*5
	int cnt = calculation(str);
	cout<<cnt;
	return 0;
}

也有想过对输入的字符串进行一个合法性的判断,括号匹配,正确的运算符,这两个判断可以简单实现,但下面这两种

1234-1234+-34
12+231*41-

感觉判断起来有些许繁琐,但不判断程序又显得不完美,小记录一下。多给给几组测试样例,因为是整数相除,所以尽量选择整除的数:

	//(56-20)/(4+2)
	//9*(10+11*(12+13))+14
	//(15+16)*(17+18*(19+20))
	//(((21+22)*23+(24*25))+26*27)+28	
	//10+11*12/12+14-1+2*3*1-5/5*5-5-5-5*5/5*5/5
	//10+133+(4+22)/2*5
	//10+11-12/12+14-1+22/2*5

有问题敬请大家指正。

猜你喜欢

转载自blog.csdn.net/weixin_43912621/article/details/127187712