栈的三个经典问题--《算法笔记》同步与补充

栈的经典问题

逆波兰式问题(后缀表达式),括号匹配问题,序列输出问题

逆波兰式

问题概述:

题目给出中缀表达式,要求计算出相应的结果
题目链接

题目思路:

1、中缀表达式转换为后缀表达式
原因:中缀表达式适合人类思维,但计算机很难处理,通常要转化为后缀表达式,利用栈处理
2、计算后缀表达式

中缀转后缀的具体思路:

1、设置操作符栈(通过比较操作符的优先级,确定是否出栈),设置队列来存放后缀表达式
2、扫描遍历中缀表达式,遇到操作数放入队列,遇到操作符则采取下列方式处理:
2.1、如果碰到的操作符优先级高于栈顶操作符优先级,则将碰到的操作符压入栈中
2.2、如果碰到的操作符优先级低于或等于栈顶操作符优先级,则不断出栈,并存入队列,直到碰到的操作符优先级高于栈顶操作符的优先级
2.3、如果碰到括号,遇到左括号直接压入栈中,遇到右括号,不入栈,并且不断弹出栈中操作符,直到碰到左括号,此时抛弃左括号
3、重复上述操作,直到中缀表达式遍历结束,若操作符栈不空,则全部弹出存入队列

题目细节:

1、对于中缀字符串的处理,涉及字符串的分割与拼接
2、后缀表达式需要栈来配合处理,其中弹出的第一个数字是num2,第二个数字是num1

#include 
using namespace std;
struct node{
	double num;
	char op;
	bool flag;
};
string str;
stack<node> s;
queue<node> q;
map<char, int> op;
//中缀转后缀 
void Change()
{
	double num;
	node temp;
	for(int i = 0; i < str.length();)//暂不定义for执行后i的变化情况 
	{
		if(str[i] >= '0' && str[i] <= '9')//是操作数 
		{
			temp.flag = true;
			temp.num = str[i++] - '0';
			while(i < str.length() && str[i] >= '0' && str[i] <= '9')
			{
				temp.num = temp.num * 10 + str[i] - '0';
				i++;
			}
			q.push(temp);
		}
		else//是操作符 
		{
			temp.flag = false;
			temp.op = str[i];
			//关于括号 
			if(str[i] == '(')
			{
				s.push(temp);
				i++;
				continue;
			}
			if(str[i] == ')')
			{
				while(!s.empty() && s.top().op != '(')
				{
					q.push(s.top());
					s.pop();
				}
				s.pop();
				i++;
				continue;
			}
			while(!s.empty() && op[s.top().op] >= op[str[i]])
			{
				q.push(s.top());
				s.pop();
			} 	
			s.push(temp);
			i++;
		}
	}
	while(!s.empty())
	{
		q.push(s.top());
		s.pop();
	}
	//输出后缀表达式 
	/*while(!q.empty())
	{		
		if(q.front().flag == true)
		{
			cout << q.front().num;
		}
		else
		{
			cout << q.front().op;
		}
		q.pop();
	}
	printf("\n");*/
}
double compute(double n1, double n2, char c)
{
	switch(c)
	{
		case '+':
			return n1 + n2; 
			break;
		case '-':
			return n1 - n2;
			break;
		case '*':
			return n1 * n2;
			break;
		case '/':
			return n1 / n2;
			break;
	}
}
double calculate()
{
	double temp1, temp2;
	node cur, temp;
	while(!q.empty())
	{
		cur = q.front();
		q.pop();
		if(cur.flag == true)//是操作数 
		{
			s.push(cur);
		} 
		else
		{
			temp2 = s.top().num;
			s.pop();
			temp1 = s.top().num;
			s.pop();
			temp.flag = true;
			temp.num = compute(temp1, temp2, cur.op);
			s.push(temp);
		}
	}
	return s.top().num;
} 
int main(int argc, char *argv[]) {
	op['+'] = op['-'] = 1;
	op['*'] = op['/'] = 2;
	while(getline(cin, str), str != "0")
	{
		for(string::iterator it = str.end(); it != str.begin(); it--)//倒叙 
		{
			if(*it == ' ')
			{
				str.erase(it);
			}
		}
		while(!s.empty())//清空栈,初始化栈 
		{
			s.pop();
		}
		Change();
		double ans = calculate();
		printf("%.2f\n", ans);
	}
	return 0;
}

括号匹配问题:

题目链接
错误:[(]) 正确:([]{})

思路:

遇到左括号全部入栈,遇到右括号,判断栈顶元素是相应左括号则弹出,否则return false(对三种括号单独判断),最后栈为空,则互相匹配 return true,栈非空则return false

个人思路:

参考后缀表达式处理括号的方式
遇到左括号直接入栈
遇到右括号,不断弹出栈顶元素(对三种括号单独判断处理)

#include 
using namespace std;
int N;
string str;
int main(int argc, char *argv[]) {
	while(scanf("%d", &N) != EOF)
	{
		while(N--)
		{
			stack<char> s;
			bool cmplt = true;
			cin >> str;
			int len = str.length();
			if(len & 1)
			{
				cmplt = false;
			}
			for(int i = 0; i < len; i++)
			{
				if(!cmplt)
				{
					break;
				}
				if(str[i] == '(' || str[i] == '[' || str[i] == '{')
				{
					s.push(str[i]);
				}
				else if(str[i] == ')')
				{
					//不空且没有遇到左括号 
					while(!s.empty() && s.top() != '(')
					{
						s.pop();
					}
					if(s.empty())//栈空 
					{
						cmplt = false;
					}
					else//找到左括号 
					{
						s.pop();//左括号弹出 
						continue;
					}
				}
				else if(str[i] == ']')
				{
					while(!s.empty() && s.top() != '[')
					{
						s.pop();
					}
					if(s.empty())//栈空 
					{
						cmplt = false;
					}
					else//找到左括号 
					{
						s.pop();//左括号弹出 
						continue;
					}
				}
				else if(str[i] == '}')
				{
					while(!s.empty() && s.top() != '{')
					{
						s.pop();
					}
					if(s.empty())//栈空 
					{
						cmplt = false;
					}
					else//找到左括号 
					{
						s.pop();//左括号弹出 
						continue;
					}
				}
			}
			if(!cmplt)
			{
				printf("no\n");
			}
			else
			{
				printf("yes\n");
			}
		}
	}
	
	return 0;
}

序列顺序

题目链接

个人思路:

初始化:将1入栈
遍历给出的序列,只要栈顶元素与当前序列位置的元素不等时,便继续入栈,序列下标不动;若栈顶与此时序列元素相等,则出栈,下标加1
当超过栈容量或数字超过给定范围,则此顺序错误;若序列遍历顺利结束,说明顺序合理

#include 
using namespace std;
int m, n, k;
int main(int argc, char *argv[]) {
	scanf("%d%d%d", &m, &n, &k);
	while(k--)
	{
		stack<int> s;
		int arr[1005] = {0};
		int num = 1, index = 0;
		bool cmplt = true;
		for(int i = 0; i < n; i++)
		{
			scanf("%d", &arr[i]);
		}
		s.push(num++);//1入栈 
		for(int j = 0; j < n; j++)
		{
			if(s.empty())
			{
				s.push(num++);
			}
			if(s.top() == arr[j])//输出序列和栈顶元素相同 
			{
				//cout << "***" << s.top() << endl;
				s.pop();
			}
			else
			{
				if(num <= n)
				{
					s.push(num++);
					j--;
				}
				else
				{
					cmplt = false;
					break;
				}
			}
			if(s.size() > m)
			{
				cmplt = false;
				break;
			} 
		}	
		if(cmplt)
		{
			printf("YES\n");
		}		
		else
		{
			printf("NO\n");
		}
	}
	return 0;
}

你可能感兴趣的:(算法笔记)