栈与回溯法 - 鲁迅外国语学校网站>> 首页

Download Report

Transcript 栈与回溯法 - 鲁迅外国语学校网站>> 首页

栈的定义
栈是限制在表的一端进行插入和删除运算的
线性表。通常称插入、删除的这一端为栈顶,另
一端为栈底。
当表中没有元素时称为空栈。
栈顶
例:一叠书或一叠盘子。
a
a
n
n-1
……
栈底
鲁迅外国语学校 2010.11
a2
a1
设栈S=(a1,a2,a3,…an),则a1为栈底元素,an
为栈顶元素。入栈序列为a1,a2,a3,…,an,出
栈序列为an,… a3 ,a2 ,a1 。 进栈 出栈
栈的基本特征:
先进后出(FILO)。
栈顶
an
….
a2
栈底
鲁迅外国语学校 2010.11
a1
顺序栈
用顺序方法存储的栈称为顺序栈。


可用一组地址连续的存储单元依次存放自栈底
到栈顶的数据元素,一般用一维数组表示。
另设一个位置指针top(栈顶指针)来动态地
指示栈顶元素在顺序栈中的位置。
top
a2
a1
鲁迅外国语学校 2010.11
在PASCAL语言中可以这样描述栈:
const
maxlen=栈的容量;
type
stacks=record
data:array[1..maxlen] of datatype;
top:0..maxlen;
end;
var
s:stacks;
鲁迅外国语学校 2010.11
栈的基本操作
9
8
入栈算法:
procedure push(x:datatype;var s:stacks);
begin
if s.top=maxlen then
begin
writeln(‘overflow’); {上溢}
halt
end
else begin
s.top:=s.top+1;
s.data[s.top]:=x;
end
end;
鲁迅外国语学校 2010.11
7
6
5
top
4
a4
3
2
a3
1
a1
a2
栈的基本操作
9
8
入栈算法:
procedure push(x:datatype;var s:stacks);
begin
if s.top=maxlen then
begin
writeln(‘overflow’); {上溢}
halt
end
else begin
s.top:=s.top+1;
s.data[s.top]:=x;
end
end;
鲁迅外国语学校 2010.11
7
6
top
5
4
a4
3
2
a3
1
a1
a2
栈的基本操作
9
8
入栈算法:
procedure push(x:datatype;var s:stacks);
begin
if s.top=maxlen then
begin
writeln(‘overflow’); {上溢}
halt
end
else begin
s.top:=s.top+1;
s.data[s.top]:=x;
end
end;
鲁迅外国语学校 2010.11
7
6
top
5
x
4
a4
3
2
a3
1
a1
a2
出栈算法:
9
8
procedure pop(var s:stacks;var x:datatype);
begin
7
if s.top=0 then
6
begin
writeln(‘underflow’);{下溢}
5
halt
end
4
else begin
x:=s.data[s.top];
3
s.top:=s.top-1;
2
end
end;
1
鲁迅外国语学校 2010.11
a4
a3
a2
a1
top
出栈算法:
9
8
procedure pop(var s:stacks;var x:datatype);
begin
7
if s.top=0 then
6
begin
writeln(‘underflow’);{下溢}
5
halt
end
4
else begin
x:=s.data[s.top];
3
s.top:=s.top-1;
2
end
end;
1
鲁迅外国语学校 2010.11
a4
x
a3
top
a2
a1
1、括号匹配的检验
设一个表达式有英文字母(小写)、运算符(+,
-,*,/)和左右圆括号构成,并以“@”作为表达式
的结束符。请编写一个程序检查表达式中的左右圆括
号是否匹配,若匹配,则返回“YES”;否则返回
“NO”。表达式长度小于255,左圆括号少于20个。
鲁迅外国语学校 2010.11
分析:
假设输入的字符串存储在字符串c中,我们可以定义一个栈,用
它来存放表达式中从左往右的左圆括号。
var s:array[1..maxn] of char;
top:integer;
算法:
从左往右依次扫描表达式的每个字符c[i],若是“(”,则让它
进栈;若遇到的是“)”,则让栈顶元素出栈;当栈发生下溢或当表
达式处理完毕而栈非空时,表示不匹配,返回“NO”;否则表示匹配
,返回“YES”。
鲁迅外国语学校 2010.11
var c:string;
')':if top>0
function doit(c:string):boolean;
then( top:=top-1
else begin
( doit:=false );
var s:array[1..20] of char;
top,i:integer; ch:char;
exit;
begin
end;
( top:=0
);
end;
i:=1; ch:=c[i];
i:=i+1; ch:=c[i];
while ch<>'@' do
end;
if ( top=0 )then doit:=true
begin
if (ch='(') or (ch=')') then
case ch of
else doit:=false;
end;
'(':begin
begin
top:=top+1;(s[top]:='(');
end;
)
readln(c);
鲁迅外国语学校
end. 2010.11
writeln(doit(c));
2、计算后缀表达式的值
所谓后缀表达式是指这样的一种表达式:式中不再
引入括号,运算符放在两个运算对象之后。所有计算按
运算符出现的顺序,严格地由左而右进行,不再考虑运
算符的优先规则。例如5*(7-3)+9对应的后缀表达式
为5 7 3 -*9 +,其中每个操作数后都有一个空格。
输入:后缀表达式a
输出:表达式的值
鲁迅外国语学校 2010.11
分析:
设后缀表达式串为a,操作数、中间结果和最终结果
都存放在栈S中,S的元素类型为实型。
计算过程如下:由左向右处理a中的每一个字符。若
遇到一个操作数,就送入栈S中保存;遇到一个操作符,
就从栈中取出栈顶的两个操作数进行计算,然后将计算
结果重新压入栈中。依次类推,直至表达式最后一个操
作符处理完毕,这时的栈顶元素值即为最终计算结果。
鲁迅外国语学校 2010.11
表达式:5 7 3 - * 9 +
top
鲁迅外国语学校 2010.11
表达式:5 7 3 - * 9 +
top
5
鲁迅外国语学校 2010.11
表达式:5 7 3 - * 9 +
top
7
5
鲁迅外国语学校 2010.11
表达式:5 7 3 - * 9 +
top
3
7
5
鲁迅外国语学校 2010.11
表达式:5 7 3 - * 9 +
3
top
7
5
鲁迅外国语学校 2010.11
表达式:5 7 3 - * 9 +
7
top
-
5
鲁迅外国语学校 2010.11
3 =4
表达式:5 7 3 - * 9 +
top
4
5
鲁迅外国语学校 2010.11
表达式:5 7 3 - * 9 +
4
top
5
鲁迅外国语学校 2010.11
表达式:5 7 3 - * 9 +
5
top
*
4 =20
鲁迅外国语学校 2010.11
表达式:5 7 3 - * 9 +
top
20
鲁迅外国语学校 2010.11
表达式:5 7 3 - * 9 +
top
9
20
鲁迅外国语学校 2010.11
表达式:5 7 3 - * 9 +
9
top
20
鲁迅外国语学校 2010.11
表达式:5 7 3 - * 9 +
20 + 9 =29
top
鲁迅外国语学校 2010.11
表达式:5 7 3 - * 9 +
top
29
鲁迅外国语学校 2010.11
type
stack=record
data:array[1..100]of real;
top:0..100;
end;
var
s:stack;
i:integer; x:real; a:string; ch:char;
function pop (var s:stack):real; {出栈}
begin
pop:=s.data[s.top]; s.top:=s.top-1;
end;
procedure push (var s:stack;x:real); {入栈}
begin
s.top:=s.top+1; s.data[s.top]:=x;
end;
begin {主程序}
readln(a); s.top:=0; {置空栈}
鲁迅外国语学校 2010.11
i:=1; ch:=a[i];
while (
i<=length(a)
)do
begin
case ch of
‘0’..‘9’: begin {产生完整的数}
x:=0;
ch<>' '
while(
) do
begin
x:=x*10+ord(ch)-ord('0'); i:=i+1; ch:=a[i];
end;
end;
'+‘ : x:=pop(s)+pop(s);
‘-’ : begin x:=pop(s); x:=pop(s)-x; end;
'*‘ : x:=pop(s)*pop(s);
‘/’ : begin x:=pop(s); x:=pop(s)/x; end;
end;
( push(s,x)
);
i:=i+1; ch:=a[i]; {继续扫描字符串a}
end;
writeln(
end.
pop(s)
:0:0);
鲁迅外国语学校 2010.11
递归在计算机中的实现
计算机执行递归算法时,也是通过栈来实现的。在递归
过程(函数)开始运行时,系统首先为它建立一个栈,该栈
的元素类型包括值参、局部变量和返回地址;在每次执行递
归调用语句之前,系统自动把本算法中所使用的值参和局部
变量的当前值以及调用后的返回地址压入栈中,在每次递归
调用结束后,又自动把栈顶元素的值分别赋给相应的值参和
局部变量(出栈),以便使它们恢复到调用前的值,接着无
条件返回由返回地址所指定的位置继续执行算法。
鲁迅外国语学校 2010.11
栈与回溯法
回溯是搜索算法中的一种控制策略。它的基本思想
是:按照某种条件往前试探搜索,若前进中遭到失败,
为了摆脱当前的失败状态,返回搜索步骤中的上一个结
点,并重新寻求解答。
在前面,我们已经学习了用递归来实现回溯算法。
其实,回溯法的程序也可以改成非递归的、用栈模拟执
行。
实现方法:
1.用栈保存好前进中的某些状态。
2.制定好约束条件。
鲁迅外国语学校 2010.11
例1:四色问题:
设有下列形状的图形:有n个区域(1≤n≤100),各区域的相邻关
系用0(不相邻)、1(相邻)表示。如以下图表。将上面图形的每一部
分涂上红(1)、黄(2)、蓝(3)、绿(4)四种颜色之一,要求相邻
部分的颜色不同。
1
2
3
4
5
6
7
8
1
0
1
0
0
0
0
1
1
2
1
3
0
1
0
1
0
1
0
0
4
0
0
1
0
1
1
0
0
5
6
0
1
0
0
1
1
0
4
7
3
8
2
5
0
0
0
1
0
1
0
0
6
0
1
1
1
1
0
1
0
7
1
1
0
0
0
1
0
1
8
1
0
0
0
0
0
1
0
鲁迅外国语学校 2010.11
1
分析:设m为图的邻接矩阵。按照涂色的先后顺序将方案存入堆栈s中,其中
s[k]为区域k的颜色码,区域k为第k个涂色的区域。在计算过程中,我们必须记住当
前涂色的区域号i,该区域称之为栈顶,区域准备涂颜色j(1≤j≤4)。
首先将区域1涂红色(s[1]:=1),并准备涂区域2(i:=2),从颜色1开始试探(
j:=1)。区域 i 能否涂颜色j,关键取决于区域 i 的周边是否有同色区域。搜索已涂色
的区域1~ 区域i-1中与区域 i 相邻的区域k,看一看这些区域是否涂了颜色j。搜索结
束后,若 t 值为真,则说明与区域 i 相邻的区域 k已经涂了颜色j,按照颜色码递增顺
序换一种颜色(j:=j+1);否则说明区域 i 的周边没有涂颜色 j 的区域,区域 i 可以
涂颜色j(s[i]:=j)。"入栈",准备涂区域i+1,从颜色1开始试探(i:=i+1,j:=1)。
对区域i,按照颜色码递增顺序试探颜色。如果区域 i 找不到合适的颜色(j>4)
则应该出栈,按照颜色码递增顺序调整区域i-1的颜色(i:=i-1,j:=s[i]+1)。
如此,从区域1出发,依次类推,直至区域n涂好颜色为止(i>n)。
鲁迅外国语学校 2010.11
var
i,j,k,n:integer;
t:boolean;
m:array[1..100,1..100] of 0..1;{图的邻接矩阵}
s:array[1..100] of integer;
{栈,存放区域的颜色表}
begin
readln(n);
for i:=1 to n do
begin
for j:=1 to n do read(m[i,j]);readln;
end;
s[1]:=1;
{区域1涂红色}
i:=2;j:=1;{指向区域2,从颜色1开始试探}
鲁迅外国语学校 2010.11
if j>4 then
while i<=n do
begin
while (j<=4)and(i<=n) do {寻找区域i 的颜色}
begin
begin
i:=i-1;{出栈}
{判断已涂颜色的区域1~区域i-1中是否存在与
区域 i相邻,且已涂了颜色j的区域}
( t:=false);
for k:=1 to i-1 do
if (m[i,k]=1)and( s[k]=j ) then
begin
t:=true;break;
end;
if t then( j:=j+1 ) {换一种颜色 }
else begin {入栈}
s[i]:=j;
i:=i+1;
( j:=1 );
end;
end;
if i=0 then
begin
writeln('noanswer');halt;
end;
( j:=s[i]+1 );
end;
end;
for i:=1 to n do writeln(i,'--->‘,s[i]);
end.
鲁迅外国语学校 2010.11
例2:全排列:
由键盘上输入任意n个符号,输出它的全排列。
下面以输入1234为例:
1234 1243 1324 1342 1423 1432 2134 2143
2314 2341 2413 2431 3124 3142 3214 3241
3412 3421 4123 4132 4213 4231 4312 4321
鲁迅外国语学校 2010.11
procedure print;
const
var i:integer;
n=4;
begin
for i:=1 to n do write( st [ x[i] ] ); writeln;
var
i,k:integer;
st:string[n];
x:array[1..n] of integer;
function place( k:integer ):boolean;
var i:integer;
begin
end;
begin
readln( st );
k:=1; x[k]:=0;
while( k>0 ) do
begin
x[k]:=x[k]+1;
place:=true;
while (x[k]<=n) and (
if x[k]>n then(
else begin
place:=false;
k:=k+1; ( x[k]:=0 )
break
end;
k:=k-1 )
else if k=n then print
begin
end ;
) do
x[k]:=x[k]+1;
for i:=1 to k-1 do
if (x[i]=x[k] )then
not place(k)
end
end ;
end.
鲁迅外国语学校 2010.11
procedure print;
const
var i:integer;
n=4;
begin
for i:=1 to n do write( st [ x[i] ] ); writeln;
var
i,k:integer;
st:string[n];
x:array[1..n] of integer;
function place( k:integer ):boolean;
end;
begin
readln( st );
k:=1; x[k]:=0;
while( K<=n ) do
var i:integer;
begin
begin
x[k]:=x[k]+1;
while ( not place(k)
place:=true;
x[k]:=x[k]+1;
for i:=1 to k-1 do
k:=k+1; (x[k]:=0 )
if (x[i]=x[k] )then
end ;
begin
place:=false;
break
print;
end.
end ;
end;
鲁迅外国语学校 2010.11
) do
例3:编程解决八皇后问题:要求在国际象棋棋盘上放置八个皇后
,使她们不能互相攻击,即任何两个皇后不能处在同一行、同一列、
同一条线上。请找出所有的摆法。
分析:
如果我们把8*8的棋盘看成是一个平面直角坐标系,那么任意两
个皇后在平面上的坐标应同时满足以下三个条件:
⑴两个皇后的横坐标不相等。 I≠K
⑵两个皇后的纵坐标不相等。当I≠K时,X[I] ≠ X[K]
⑶两个皇后的横坐标之差的绝对值不等于纵坐标之差的绝对值。
当I≠K时,|I-K|≠|X[I]-X[K]|
我们用数组x[i]来描述八个皇后在棋盘上的状态, x[i] =j表示
在第i行的第j列放置了一个皇后。
鲁迅外国语学校 2010.11
const n=8;
var
begin
k:=1;
i,j,k:integer;
x:array[1..n] of integer;
function place(k:integer):boolean;
var i:integer;
begin
place:=true;
for i:=1 to k-1 do
if (x[i]=x[k] ) or (abs(x[i]-x[k])=abs(i-k))
x[k]:=0;
while ( k>0 )do
begin
x[k]:=x[k]+1;
while(x[k]<=n)and( not place(k) ) do
x[k]:=x[k]+1;
if ( x[k]>n )then k:=k-1
else if k=n then ( print
else begin
then place:=false ;
end;
procedure print;
var i:integer;
begin
for i:=1 to n do write(x[i]:4);
writeln;
end;
( k:=k+1 );
x[k]:=0
end
end ;
end.
鲁迅外国语学校 2010.11
)