关于列车长的烦恼一题的思考
nencc 2011-05-09 07:38:57 关于列车长的烦恼问题前几天有人给发我了这样的代码,计算列车长的烦恼问题。此代码可以正常运行,且计算正确,代码非常短。
int main()
{
scanf("%d",&cases);
while(cases--)
{ scanf("%d",&n);
for(i=1;i<=n;i++) scanf("%d",&a[i]);
top=0;p=1;aa=1;stack[0]=-1;
while(p<n+1)
{
stack[++top]=p;p++;
while(stack[top]==a[aa]) { top--;aa++; } }
if(top==0) printf("Yes\n");
else printf("No\n"); }
return 0;
}
对代码分析后,初步结论:
一、代码短小原因
1.未构造类作为栈,而是只用数组stack和下表top进行操作,其实一个简单的栈也正是包含这两个因素。这样会单单是栈就会减少10行左右的代码。但是,这样的设计并不影响本质,即:自己构造一个完备的栈类丝毫不必直接使用数组会差,故并不推荐直接数组。直接使用数组在问题简单的情况下是可以的,但是问题复杂后,直接使用数组就不再那么有效。起码会让人觉得很累。(分析结果:不推荐此方法)
2.在判断分支时不够全面详细,虽然代码可以正确完成任务,但由于判断分支存在缺漏,会导致程序判断效率不高。代码短效率反而不高?是的,因为缺少了判断分支,某些情况下没有及时得出结果,而是简单的去判断下一个。后面我将画出此代码的流程图参考。(分析结果:简化了判断分支,代码短小但导致了效率的降低)
3.部分代码将多行代码写到一行中,看上去比实际的更短。(分析结果:不推荐此方法)
4.部分变量名用简单的a,p,aa,这种无确切含义的短名字,使代码看上去比实际的更短。(分析结果:不推荐此方法)
二、此代码核心算法与结构图
第一步:车厢进栈(因为是首节车厢,栈为空,除了进栈外别无选择)
第二步:比对栈顶车厢是否同输出序列车厢相符,若相符则出栈,比对序列车厢下一个
第三步: 继续进行第二步,直到出现不相等情况。
第四步:出现不相等时,回到第一步,右边车厢进栈(直到超出输入的车厢总数目)
第五步:判断栈内是否为空,为空则正确,不为空说明出错。
归纳一句话,比对是否相等,若相等则出栈,若不等则进栈。
因为栈来说,要么进栈要么出栈。车厢来说要么进来,要么出去。所以程序逻辑认为,如果比对不相符,那么一定需要进栈,如果相符那么出栈。这是正常的情况确实这样。但如果输出序列是错误的呢?
比如大家直到312是个不可能的序列,按照以上逻辑,一开始比对数字是3,
1:进栈。 栈内数据:1
2:比对,3!=1,继续进栈。栈内数据:1,2
3:比对,3!=2,继续进栈。栈内数据:1,2,3
4:比对,3==3,出栈, 栈内数据:1,2
5:比对1!=2(因为输出序列第一个3已经匹配,继续比对输出序列下一个1,栈顶为2,比对不符) ,进栈, 因为总数是3,已无车厢进栈。结束判断。
6:判断栈是否为空,栈内元素为1,2,显然不为空,故判断出此序列是错误的。
int a[110],stack[110],cases,n,i,p,top,aa; 三、程序存在问题举例:由于312是不可能序列,若输出序列为:3,1,2,4,5,6,7,8,9.。。。。。。
一个比较长的序列,假设有100节车厢,输出序列是前面是3,1,2,后面都是按序拍好的(或者随意排的)。
我们期望是当看到312,时就已经能够判断出错,而不必再去判断后面序列,及时退出给出结果。而按照此简洁算法逻辑执行如下:
1:进栈。 栈内数据:1
2:比对,3!=1,继续进栈。栈内数据:1,2
3:比对,3!=2,继续进栈。栈内数据:1,2,3
4:比对,3==3,出栈, 栈内数据:1,2
5:比对1!=2(因为输出序列第一个3已经匹配,继续比对输出序列下一个1,栈顶为2,比对不符) ,进栈, 栈内数据:1,2,4
6:继续比对1!=4,进栈, 栈内数据:1,2,4,5
7:继续比对1!=5,进栈, 栈内数据:1,2,4,5,6
8:继续比对1!=6,进栈, 栈内数据:1,2,4,5,6
9:继续比对1!=7,进栈, 栈内数据:1,2,4,5,6,7
。。。。。。。
一直比对到100,最后栈内数据是:1,2,4,5,6,7.。。。。。。100
程序最后判断栈是否为空,结果栈内确实很满,此时才给出不匹配的结论。。。。。
其实我们是期望在第5处就能及时发现不匹配,而不用再往下进行了。能够及时退出的算法即流程图上次我已经发给你了,可以作为参考。就是多出2个判断分支。这里只做相等的判断,在那里我还做了大于,小于的情况分别进行判断,能够最快的得出结论。