第三章 实践2 学习心得(顺序栈+数组)
题目及sample如下:
7-1 出栈序列的合法性 (20分)
给定一个最大容量为 M 的堆栈,将 N 个数字按 1, 2, 3, ..., N 的顺序入栈,允许按任何顺序出栈,则哪些数字序列是不可能得到的?例如给定 M=5、N=7,则我们有可能得到{ 1, 2, 3, 4, 5, 6, 7 },但不可能得到
{ 3, 2, 1, 7, 5, 6, 4 }。
输入格式:
输入第一行给出 3 个不超过 1000 的正整数:M(堆栈最大容量)、N(入栈元素个数)、K(待检查的出栈序列个数)。最后 K 行,每行给出 N 个数字的出栈序列。所有同行数字以空格间隔。
输出格式:
对每一行出栈序列,如果其的确是有可能得到的合法序列,就在一行中输出YES
,否则输出NO
。
输入样例:
5 7 5
1 2 3 4 5 6 7
3 2 1 7 5 6 4
7 6 5 4 3 2 1
5 6 4 3 7 2 1
1 7 6 5 4 3 2
输出样例:
YES
NO
NO
YES
NO
先总结一下自己在第一遍敲代码时的错,一开始的思路是:按顺序将元素入栈,直至栈满。把栈顶元素跟需要检验的出栈序列相比较,如果相等,则出栈。最后检验是否栈空,如果是,就输出YES,否则输出NO。错误代码不堪入目就不贴上来啦。之所以做错的原因是:搞不清楚入栈和把栈顶数字做比较那一块要怎么做。一开始觉得这两个操作应该有个时间差,得先入栈完才能比较;但是栈的容量不够,根本没办法入栈完。然后就一边入栈一边比较,但是这样的话自己思路全乱了,嵌套了4、5个循环。
后来看了别人的博客上AC的代码,边看边敲,写了详细的注释,AC代码如下:
#include <iostream>
#include <bits/stdc++.h> //包含 stack<int> 的头文件
using namespace std ;
stack<int> s ;
#include <bits/stdc++.h> //包含 stack<int> 的头文件
using namespace std ;
stack<int> s ;
int main()
{
int m, n, k ;
cin >> m >> n >> k ;
//m: 堆栈最大容量 ; n: 入栈元素个数 ; k: 待检查的出栈序列个数
int a[1005] ; //保险起见,将数组容量定义到1005
int t1, t2 ;
while(k--)
{
//每次循环重新开始时,如果栈非空,需要先清空栈!!
while(!s.empty())
{
s.pop() ;
}
t1 = 1 ; //t1: 可以理解为压入栈的数字(1,2,3,4...,n)
t2 = 1 ; //t2: 可以理解为到数组的第几个位置(下标加1)
for(int i=1 ; i<=n ; i++)
{//输入 需要检验的出栈序列
cin >> a[i] ;
}
int flag = 1 ; //用于判断一种错误的特殊情况,此情况下出错栈非空,无法用s.empty()判断
while(1)
{
if(t1 == a[t2])
{
t1++ ;
t2++ ;
}
else if(!s.empty() && s.top() == a[t2])
{ //条件:栈非空、栈顶数字与出栈序列当前的数字相等,
//结果:出栈,且t2++,出栈序列往后挪一位继续检查
s.pop() ;
t2++ ;
}
else
{ //这里判断两种特殊情况:
/*1)如果应该压入栈的数字已经大于n,说明1到n的数字全都经过了检验,此时循环可以停止
2)如果栈的长度大于规定好的容量m,说明出栈序列是错的,flag变为0,停止循环
例如:m=5,而出栈序列为 6,5,4... 则错误*/
if(t1 > n) break ;
s.push(t1) ;
t1++ ;
if(s.size() >= m)
{
flag = 0 ;
break ;
}
}
}
if(!flag || !s.empty())
{
cout << "NO" << endl ;
}
else cout << "YES" << endl ;
{
int m, n, k ;
cin >> m >> n >> k ;
//m: 堆栈最大容量 ; n: 入栈元素个数 ; k: 待检查的出栈序列个数
int a[1005] ; //保险起见,将数组容量定义到1005
int t1, t2 ;
while(k--)
{
//每次循环重新开始时,如果栈非空,需要先清空栈!!
while(!s.empty())
{
s.pop() ;
}
t1 = 1 ; //t1: 可以理解为压入栈的数字(1,2,3,4...,n)
t2 = 1 ; //t2: 可以理解为到数组的第几个位置(下标加1)
for(int i=1 ; i<=n ; i++)
{//输入 需要检验的出栈序列
cin >> a[i] ;
}
int flag = 1 ; //用于判断一种错误的特殊情况,此情况下出错栈非空,无法用s.empty()判断
while(1)
{
if(t1 == a[t2])
{
t1++ ;
t2++ ;
}
else if(!s.empty() && s.top() == a[t2])
{ //条件:栈非空、栈顶数字与出栈序列当前的数字相等,
//结果:出栈,且t2++,出栈序列往后挪一位继续检查
s.pop() ;
t2++ ;
}
else
{ //这里判断两种特殊情况:
/*1)如果应该压入栈的数字已经大于n,说明1到n的数字全都经过了检验,此时循环可以停止
2)如果栈的长度大于规定好的容量m,说明出栈序列是错的,flag变为0,停止循环
例如:m=5,而出栈序列为 6,5,4... 则错误*/
if(t1 > n) break ;
s.push(t1) ;
t1++ ;
if(s.size() >= m)
{
flag = 0 ;
break ;
}
}
}
if(!flag || !s.empty())
{
cout << "NO" << endl ;
}
else cout << "YES" << endl ;
}
return 0 ;
}
return 0 ;
}
AC的代码里简化了需要入栈的情况,使用t1辅助比较,不需要每种情况都入栈。
然后学习了同组的珊珊的AC代码,她是用数组的做法。思路都是一样的,不过把栈变成数组stack[1007],将出、入栈变成数组下标的变化。