数据结构教程与题解
50
在以后的章节中还会经常借助栈来解决各种问题。
例 3.1 编写一个简单程序,从终端上接收字符并可进行一定的编辑。
解:假定在字符接收过程中需要以下功能:
(1)退格,即删除前面的一个字符,用字符“#”代表。
(2)作废,即删除前面的所有字符,用字符“@”表示。
(3)结束,即结束本次输入,用字符“$”表示。例如,假设从键盘上输入串“You@
How a#are You!$”,则实际上表示“How are You!”。
根据题意,可用栈来实现这个要求:若读入的字符是“#”,则退栈;若读入的字符是
“@”,则退栈到空;若读入的字符是“$”,则编辑结束;当读入其余字符时,则进栈。具
体算法如下:
sqstack sq; //顺序栈 sq 设为全程量
void edit() { //编辑好的字符串在 sq 中
char ch,x;
init_sqstack(&sq); //初始化顺序栈 sq
while(cin>>ch,ch!=’$’) { //读入一个字符,若不是结束符则循环
switch(ch) {
case ’#’: pop_sqstack(&sq,&x);break;
case ’@’: while(!empty_sqstack(&sq))
pop_sqstack(&sq,&x); break; //清空栈
default: push_sqstack(&sq,ch);
}
}
}
例 3.2 设有 A、B、C、D 四辆车依
次进入一个栈式结构的车库,问从库中倒出来的
车可不可能是 A、D、B、C 的顺序。
解:这里规定了入库即进栈次序,并不意味着已入栈的车中途不能出栈,也就是说进
栈、出栈可交替进行。为判断出栈顺序的可能性,一个简单的方法是用图形来模拟,即先画
一个空栈,然后根据进出栈序列的要求来模拟相应的进出栈过程。对目标序列{A, D, B, C}:
(1)第一个出栈的是 A,这可通过 A 入栈,马上出栈得到。
(2)第二个出栈的是 D,这可在(1)基础上 B 入栈、C 入栈、D 入栈、D 出栈得到。
(3)第三个要让 B 出栈,这不可能了,因为第(2)步后栈内有 B 和 C,但 C 为栈顶,
只有 C 出栈了 B 才可能出栈。所以{A, D, B, C}不是可能的出栈序列。
如果涉及的序列元素较多,用上面的方法来模拟就比较烦琐了。实际上,这类问题是
有规律可循的:设 n 个元素按大小依次入栈,则在可能的出栈序列中,对任一元素 k,其
后面的元素或者都大于 k;或者小于 k 的元素递减排列(参见习题 3.2)。对本例{A, D, B, C},
因 D 后面比它小的{B, C}不是递减排列,故不是可能的出栈序列;又如{A, C, B, D}则是可能
的出栈序列,相应的进出栈过程为:A 进→A 出→B 进→C 进→C 出→B 出→D 进→D 出。
与此例相关的另一类问题是,当入栈次序一定时,可能的出栈序列有多少。显然,n
个
数据的排列有 n!种,但未必都是可能的出栈顺序。当元素很少时可逐个检查其排列,
如 3 个元素{A, B, C}的排列有 3!=6 种:ABC、ACB、BAC、BCA、CAB、CBA,检查发
现 CAB 不可能,故可能的出栈序列有 5 种。当元素较多时,逐个检查其排列就不合适了。