题目链接地址:
九度OJ-题目1366:栈的压入、弹出序列
题目描述:
输入两个整数序列,第一个序列表示栈的压入顺序,请判断第二个序列是否为该栈的弹出顺序。假设压入栈的所有数字均不相等。例如序列1,2,3,4,5是某栈的压入顺序,序列4,5,3,2,1是该压栈序列对应的一个弹出序列,但4,3,5,1,2就不可能是该压栈序列的弹出序列。
输入:
每个测试案例包括3行:
第一行为1个整数n(1<=n<=100000),表示序列的长度。
第二行包含n个整数,表示栈的压入顺序。
第三行包含n个整数,表示栈的弹出顺序。
输出:
对应每个测试案例,如果第二个序列是第一个序列的弹出序列输出Yes,否则输出No。
样例输入:
5
1 2 3 4 5
4 5 3 2 1
5
1 2 3 4 5
4 3 5 1 2
样例输出:
Yes
No
解题思路:
开始看到这道题的时候,我的想法是如果弹出序列中的部分子序列与压入序列中的部分子序列相同,就认为弹出序列不合法。
显然这种想法是错误的 ╮(╯▽╰)╭
想了很久也想不到解法,只能求助范神了。大神就是不一样,分分钟就给出了算法。该算法的大致思想是通过压入序列和弹出序列来动态地模拟栈的压入和弹出过程,在模拟操作结束后,如果栈为空则可以认为弹出序列是合法的,否则就认为弹出序列不合法。
下面介绍一下算法的具体流程:
(1) 依次遍历压入序列和弹出序列;
(2) 先将被遍历的压入序列元素压入到数据栈中,如果当前被遍历的压入序列元素等于当前被遍历的弹出序列元素,则停止压入操作,否则接着遍历压入序列的下一个元素;
(3) 如果数据栈的栈顶元素与当前被遍历的弹出序列元素相同,则弹出数据栈的栈顶元素,接着遍历弹出序列中的下一个元素;
(4) 重复执行(1),(2),(3),当遍历完压入序列或弹出序列后,停止遍历操作。此时如果数据栈为空,则证明弹出序列是合法的;否则证明弹出序列是不合法的。
以第1个测试用例为例:
压入序列为:1 2 3 4 5
弹出序列为:4 5 3 2 1
模拟压入和弹出操作的过程如下所示:
1) 压入元素1,2,3与弹出元素4不相等,将1, 2, 3依次压入到数据栈中,将4压入数据栈后发现4与弹出序列中的第一个元素4相等,暂停压入操作,此时数据栈的栈顶元素是4;
2) 因为数据栈栈顶元素4与弹出序列中的第一个元素4相等,所以弹出数据栈的栈顶元素4,此时数据栈的栈顶元素是3,弹出序列中被遍历的元素是5,二者不相等,所以将压入序列中的元素5压入到栈中;
3)此时数据栈的栈顶元素是5,弹出序列中被遍历的元素是5,二者相等,所以将数据栈的栈顶元素5弹出;
4)此时数据栈的栈顶元素是3,弹出序列中被遍历的元素是3,二者相等,所以将数据栈的栈顶元素3弹出;
5)此时数据栈的栈顶元素是2,弹出序列中被遍历的元素是2,二者相等,所以将数据栈的栈顶元素2弹出;
6)此时数据栈的栈顶元素是1,弹出序列中被遍历的元素是1,二者相等,所以将数据栈的栈顶元素1弹出;
7)此时压入序列和弹出序列都已经被遍历完了,而数据栈为空,所以可以证明弹出序列是合法的。
以第2个测试用例为例:
压入序列为:1 2 3 4 5
弹出序列为:4 3 5 1 2
模拟压入和弹出操作的过程如下所示:
1)压入元素1,2,3与弹出元素4不相等,将1, 2, 3依次压入到数据栈中,将4压入数据栈后发现4与弹出序列中的第一个元素4相等,暂停压入操作,此时数据栈的栈顶元素是4;
2)因为数据栈栈顶元素4与弹出序列中的第一个元素4相等,所以弹出数据栈的栈顶元素4,此时数据栈的栈顶元素是3;
3)因为数据栈栈顶元素3与弹出序列被遍历的元素3相等,所以弹出数据栈的栈顶元素3;此时数据栈的栈顶元素是2,与弹出序列被遍历的元素5不相等,则将被遍历的压入序列元素5压入到栈中,此时数据栈的栈顶元素是5;
4)因为数据栈栈顶元素5与弹出序列被遍历的元素5相等,所以弹出数据栈的栈顶元素5,此时数据栈的栈顶元素变成了2;
5)此时数据栈的栈顶元素2与弹出序列被遍历的元素1不相等,但是压入序列中的元素都已经被压入到数据栈了。这时就陷入了“无法压入元素,也无法弹出元素”的僵持状态,而数据栈也不为空,所以可以认为弹出序列不合法。
上面的步骤看起来有些繁琐,如果照着上面的步骤画一个栈实时模拟压入和弹出操作,就能很好的理解算法的执行流程了。
AC代码如下:
// 判断栈的压入,弹出序列是否合法 #include<stdio.h> #include<stack> using namespace std; #define MAX 100000 int inStackSequence[MAX]; // 压入序列 int outStackSequence[MAX]; // 弹出序列 stack <int> dataStack; // 用于记录压入和弹出操作的数据栈 /** * 输入压入序列和弹出序列 二者的长度都为 n * @param n 表示压入序列和弹出序列的长度 * @return void */ void inputinStackSequenceAndoutStackSequence(int n) { int i; for(i = 0;i < n;i++) scanf("%d",&inStackSequence[i]); for(i = 0;i < n;i++) scanf("%d",&outStackSequence[i]); } /** * 清空数据栈 * @return void */ void clearStack() { while(false == dataStack.empty()) { dataStack.pop(); } } /** * 模拟栈的压入和弹出过程。 * 在模拟完之后,如果栈为空,则说明入栈和出栈序列是合法的; * 反之则说面入栈和出栈序列不合法。 * @param inStackSequence 压入序列 * @param outStackSequence 弹出序列 * @param inLen 压入序列的长度 * @param outLen 弹出序列的长度 * @return bool 如果弹出序列合法,则返回true;否则返回false */ bool isLegalOutStackSequence(int inStackSequence[],int outStackSequence[],int inLen,int outLen) { bool isLegal; int indexForIn = 0; int indexForOut = 0; clearStack(); // 清空数据栈 dataStack while(indexForIn < inLen && indexForOut < outLen) { // 如果当前的入栈元素不等于当前的出栈元素,则将入栈元素依次压入到数据栈中 do { dataStack.push(inStackSequence[indexForIn]); }while(indexForIn < inLen && inStackSequence[indexForIn++] != outStackSequence[indexForOut]); // 如果数据栈的栈顶元素等于当前的出栈元素,则依次弹出数据栈的栈顶元素 while(false == dataStack.empty() && dataStack.top() == outStackSequence[indexForOut]) { dataStack.pop(); indexForOut++; } }//while isLegal = dataStack.empty(); // 如果数据栈为空,则说明出栈序列是合法的,反之则说明出栈序列不合法 return isLegal; } int main() { int n; bool isLegal; while(EOF != scanf("%d",&n)) { inputinStackSequenceAndoutStackSequence(n); isLegal = isLegalOutStackSequence(inStackSequence,outStackSequence,n,n); if(true == isLegal) printf("Yes\n"); else printf("No\n"); } return 0; } /************************************************************** Problem: 1366 User: blueshell Language: C++ Result: Accepted Time:190 ms Memory:2204 kb ****************************************************************/