Tải bản đầy đủ (.pdf) (17 trang)

Lập trình với các kiểu dữ liệu hàng đợi và ngăn xếp

Bạn đang xem bản rút gọn của tài liệu. Xem và tải ngay bản đầy đủ của tài liệu tại đây (111.15 KB, 17 trang )

***Ngăn xếp và hàng ñợi***

Cấu trúc ngăn xếp và hàng ñợi
*****
Nguyễn ðức Huy
Tin Lê Khiết 2006-2009

A - Cấu trúc ngăn xếp (stack) :
I, Khái niệm :
1, ðịnh nghĩa :
-ðây là kiểu dữ liệu mà việc cập nhật và truy nhập nó ñều theo nguyên tắc "Vào sau ra
trước"( Last In, Firt Out).
2, Khai báo :
Var stack:array[1..1000] of byte;
Dst:integer;
n
Ý nghĩa : phần tử ñưa vào cuối cùng sẽ ñược lấy ra ñầu tiên.
II, Một số thủ tục và hàm cần dùng :
...
-Khởi tạo
procedure init;
2
begin
dst:=0;
1
end;
-Kiểm tra xem Stack ñã rỗng chưa
Function StackEmpty:Boolean;
Begin
StackEmpty:= (dst=0);
End;


-ðưa phần tử mới vào
procedure push(x:byte);
begin
inc(dst);
stack[dst]:=x;
end;
-Lấy phần tử trên cùng ra
function pop:byte;
begin
pop:=stack[dst];
dec(dst);
end;
-ðọc phần tử trên cùng của ngăn xếp
function get:byte;
begin
get:=stack[dst];
end;
III, Ví dụ :
Trang 1/17


***Ngăn xếp và hàng ñợi***
1. Dùng ngăn xếp ñể duyệt sâu
Procedure DFS;
Var x,i:integer;
Begin
Push(1);
Repeat
Pop(x);
For i:=n downto 1 do

If (a[x,i]=1)and(kt[i]=false) then
Begin
Push(i);
Kt[i]:=true;
End;
Until dst=0;
End;
2. ðường ñi ngắn nhất
Cho một lưới ô vuông m x n (1các ô 0 chung cạnh và không ñược qua các ô 1. Một người ở 1 ô số 0 bất kì, tìm ñường
ngắn nhất ñể người ñó ra ngoài lưới ô vuông, biết ñộ dài ñường ñi là số ô vuông nó chiếm.
Bài này mà dùng ñệ quy duyệt thì không tối ưu, ta có thể dùng 2 ngăn xếp chứa tọa ñộ
thay vì ñệ quy, dữ liệu xử lí sẽ lớn hơn rất nhiều.
Chương trình :
const u:array[1..4] of shortint=(0,0,-1,1);
v:array[1..4] of shortint=(1,-1,0,0);
var a:array[1..250,1..250] of shortint;
m,n,s,t:byte;
stx,sty:array[1..500] of integer; {---hai ngăn xếp---}
dst:integer;

{--------------------------}
function inside(x,y:byte):boolean;
begin
inside:=true;
if (x<1)or(y<1)or(x>m)or(y>n) then inside:=false;
end;

{--------------------------}
function fmin(x,y:byte):shortint;

var i:integer;
min:shortint;
begin
min:=125;
for i:=1 to 4 do
if inside(x+u[i],y+v[i]) then
if (a[x+u[i],y+v[i]]<min)and(a[x+u[i],y+v[i]]>0) then min:=a[x+u[i],y+v[i]];
fmin:=min;

Trang 2/17


***Ngăn xếp và hàng ñợi***
end;

{--------------------------}
procedure nhap;
var f:text;
i,j:integer;
c:char;
begin
assign(f,'path1.inp');
reset(f);
readln(f,m,n);
readln(f,s,t);
for i:=1 to n do
begin
for j:=1 to n do
begin
read(f,c);

if c='1' then a[i,j]:=-1;
end;
readln(f);
end;
close(f);
end;

{--------Lưu vào stack------------}
procedure savst(x,y:byte);
begin
inc(dst);
stx[dst]:=x;
sty[dst]:=y;
end;

{--------Lấy ra từ stack---------}
procedure popst(var x,y:integer);
begin
x:=stx[dst];
y:=sty[dst];
dec(dst);
end;

{--------------------------}
procedure duyet;
var x,y,i,j:integer;
begin
savst(s,t);
a[s,t]:=1;
repeat

popst(x,y);

Trang 3/17


***Ngăn xếp và hàng ñợi***
for i:=1 to 4 do
if inside(x+u[i],y+v[i]) then
if a[x+u[i],y+v[i]]=0 then
begin
savst(x+u[i],y+v[i]);
a[x+u[i],y+v[i]]:=fmin(x+u[i],y+v[i])+1;
end;
until dst=0;
end;

{--------------------------}
procedure xuat;
var i,min:integer;
f:text;
begin
assign(f,'path.out');
rewrite(f);
min:=maxint;
for i:=1 to m do
begin
if (a[i,1]<min)and(a[i,1]>0) then min:=a[i,1];
if (a[i,n]<min)and(a[i,n]>0) then min:=a[i,n];
end;
for i:=1 to n do

begin
if (a[1,i]<min)and(a[1,i]>0) then min:=a[1,i];
if (a[m,i]<min)and(a[m,i]>0) then min:=a[m,i];
end;
if min=maxint then write(f,-1)
else write(f,min);
close(f);
end;

{--------------------------}
procedure work;
begin
if a[s,t]=-1 then
begin
writeln('Test sai');
readln;
halt;
end;
duyet;
end;

{--------------------------}
BEGIN

Trang 4/17


***Ngăn xếp và hàng ñợi***
nhap;
work;

xuat;
END.
3. Tìm chu trình Euler trong một ñồ thị vô hướng bằng cách sử dụng Stack.
Chương trình :
Var a:array[1..100,1..100] of integer;
N:byte;
Procedure nhap;
Begin

{.....}
end;
function kiemtra:boolean;
var i,j:integer;
begin
kiemtra:=false;
for i:=1 to n do
begin
s:=0;
for j:=1 to n do
s:=s+a[i,j];
if s mod 2 =1 then exit;
end;
kiemtra:=true;
end;

{-------------------------------------}
procedure euler;
var ce,stack:array[1..500] of byte;
d,top:byte;
begin

top:=1;
stack[top]:=1;
repeat
v:=stack[top];
x:=1;
while (x<=n)and(a[v,x]=0) do
inc(x);
if x>n then
begin
inc(d);
ce[d]:=x;
dec(top);
end
else

Trang 5/17


***Ngăn xếp và hàng ñợi***
begin
inc(top);
stack[top]:=x;
a[v,x]:=0;
a[x,v]:=0;
end;
until top=0;
for x:=1 to d do
write(ce[x],’ ‘);
end;


{-----------------------------------------}
BEGIN
Nhap;
If kiemtra then euler
Else writeln(‘Khong co duong.’);
Readln;
End.
4. Tô màu một hình kín.
Chương trình :
Var a:array[1..250,1..250] of byte;
Stx,sty:array[1..1000] of byte;
Dst:integer;
x0,y0:byte;

{-------------------------------------}
procedure push(x,y:byte);
begin
inc(dst);
stx[dst]:=x;
sty[dst]:=y;
end;

{-------------------------------------}
procedure pop(var x,y:integer);
begin
x:=stx[dst];
y:=sty[dst];
dec(dst);
end;
{------------------------------------}

procedure fill(color:byte);
var x,y:integer;
begin
dst:=0;
push(x0,y0);
repeat

Trang 6/17


***Ngăn xếp và hàng ñợi***
pop(x,y);
if (inside(x,y))and(a[x,y]<>color) then
begin
a[x,y]:=color;
if xif x>1 then push(x-1,y);
if yif y>1 then push(x,y-1);
end;
until dst=0;
end;
5. Cho ma trận 0,1. Có thể ñi qua các ô 0, cho tọa ñộ 2 ô 0, kiểm tra xem chúng có liên
thông không.
Chương trình :
Var a:array[1..100,1..100] of byte;
n:byte;
dst:integer;
x1,x2,y1,y2:byte;
stx,sty:array[1..1000] of byte;


{-------------------------------------}
procedure push(x,y:byte);
begin
inc(dst);
stx[dst]:=x;
sty[dst]:=y;
end;

{-------------------------------------}
procedure pop(var x,y:integer);
begin
x:=stx[dst];
y:=sty[dst];
dec(dst);
end;

{-------------------------------------}
function kiemtra:boolean;
var x,y,i,j:byte;
begin
push(x1,y1);
repeat
pop(i,j); x:=i; y:=j;

{-------------}
while (a[x,y-1]=0)and(y-1>0) do
begin
a[x,y]:=1; dec(y);


Trang 7/17


***Ngăn xếp và hàng ñợi***
end; push(x,y); y:=j

{-------------}
while (a[x,y+1]=0)and(ybegin
a[x,y]:=1; inc(y);
end; push(x,y); y:=j;

{-------------}
while (a[x-1,y]=0)and(x-1>0) do
begin
a[x,y]:=1; dec(y);
end; push(x,y); x:=i;

{-------------}
while (a[x+1,y]=0)and(x<0) do
begin
a[x,y]:=1; inc(y);
end; push(x,y);

{-------------}
until (dst=0)or((stx[dst]=x2)and(sty[dst]=y2));
if dst=0 then kiemtra:=false
else kiemtra:=true;
end;
6. Khử ñệ quy thuật toán sắp xếp Quicksort

Chương trình :
{=======================}
Const Nmax=5000;
Var n, i, j, x, l, r, tg:Integer;
s:0.. Nmax;
A: Array[1..Nmax] of Integer;
Stack: Array [1.. Nmax] of Record 1 , r : Integer; End;
Procedure Sort;
Begin
S:=1;
Stack [s]. 1:=1;
Stack [s] . r :=n ;
Repeat
1:=Stack[s] . 1 ;
r: = Stack [s]. r;
Dec(s);
Repeat
i:=1;
j:=r;
x:=A[ (1+r)div 2];
Repeat
While a[i] < x Do Inc(i);

Trang 8/17


***Ngăn xếp và hàng ñợi***
While a [j] > x Do Dec (j);
if i < = j then
Begin

Tg := a [i];
a [i] : = a [j];
a[j] : = tg;
Inc(i);
Dec (j);
End;
Until i >j;
If i < r then
Begin
S : = s+1 ;
Stack [s] . 1 : = 1;
Stack [s] . r : + r ;
End;
r : = j;
Until 1> r ;
Until S= 0;
End;
7. Thuật toán tô màu ñồ thị.
Bài tập áp dụng :
1. Cho bảng vuông n x n. Tại ô (i,j) là ô ñen, còn lại ñều là ô trắng. Tìm cách ñặt sau cho
mỗi dòng, mỗi cột có ñúng 1 ô ñen.

B - Cấu trúc hàng ñợi (queue) :
B1 – Hàng ñợi bình thường :
I, Khái niệm:
1, ðịnh nghĩa :
-Khác với Stack, Queue là một kiểu dữ liệu trừu tượng mà cơ chế cập nhật và truy xuất xảy
ra ở hai ñầu khác nhau và theo quy tắc vào trước ra trước ( First In - First Out).
2, Khai báo :
Var queue:array[1..1000] of byte;

Last, first:integer;
Ý nghĩa : với cấu trúc này, phần tử ñưa vào trước ñược lấy ra trước.
* Khuyết ñiểm :ðối với hàng ñợi thường khi thêm vào ở Last và loại bỏ ở First, phần sử
dụng của hàng có khuynh hướng di chuyển về phía dưới, và ñến một lúc nào ñó thì ta
không thể thêm phần tử mới vào hàng nữa (vì số phần tử tối ña của hàng là cố ñịnh). Khi
ñó ta nói rằng hàng bị tràn.
II, Một số thủ tục và hàm cần dùng :
-Khởi tạo
Procedure Init;
Begin
First:=1;
Last:=0;
End;
Trang 9/17


***Ngăn xếp và hàng ñợi***
-Kiểm tra xem hàng ñợi ñã rỗng chưa
Function QueueEmpty : Boolean;
Begin
QueueEmpty : =First>last;
End;
-ðưa phần tử mới vào
procedure push(x:byte);
begin
inc(last);
queue[last]:=x;
end;
-Lấy phần tử dưới cùng ra
function pop:byte;

begin
If not QueueEmpty then
begin
pop:=queue[first];
inc(first);
end;
end;

n
...
2
1

B2 – Hàng ñợi vòng :
I, Khai báo :
Var queue:array[1..max] of byte;
Last, first:integer;
Hàng ñợi vòng là cải tiến của hàng ñợi bình thường ñể sử dụng tối ña mảng hàng ñợi.
II, Các thủ tục ñược cải tiến :
-ðưa phần tử mới vào
procedure push(x:byte);
begin
last:=(last+1) mod max; { Ta cho vị trí của n quay 1 vòng }
queue[last]:=x;
{ giống như 1 cây kim ñồng hồ }
end;
{ sau 1 vòng trở về chỗ cũ, ñi hết n vị trí }
-Lấy phần tử dưới cùng ra
function pop:byte;
begin

If not QueueEmpty then
N=1
begin
pop:=queue[first];
first:=(first+1) mod max;
N-1
end;
end;

B3, Bài tập ví dụ :
1. Dùng hàng ñợi ñể duyệt rộng trên ñồ thị .
Trang 10/17


***Ngăn xếp và hàng ñợi***
var a:array[1..250,1..250] of byte;
kt:array[1..250] of boolean;
Procedure BFS;
Var x,i:integer;
Begin
Push(1);
Repeat
Pop(x);
For i:=1 to n do
If (a[x,i]=1)and(kt[i]=false) then
Begin
Push(i);
Kt[i]:=true;
End;
Until first>last;

End;

2. Trên bàn cờ vua quốc tế N*N ( n<= 50) trong ñó có một số ô có mìn. Từ một ô
không có mìn cho trước con mã có thể ñi ñến một ô khác ñược hay không. Nếu ñược
hãy chỉ ra ñường ñi ngắn nhất.
File dữ liệu:
- Dòng 1 là N (kích thước bàn cờ).
- Dòng thứ i trong số N dòng tiếp theo:
* ñầu tiên là K số mìn trên dòng ñó tiếp theo là K số, mỗi số là chỉ số cột có mìn.
- Dòng cuối ghi 4 số x1, y1,x2, y2:
* (x1,y1): toạ ñộ ô xuất phát.
* (x2,y2) : Toạ ñộ ô ñích.
Chương trình :
const fi='baimin.inp';
u:array[1..8] of -2..2 =(1,2,2,1,-1,-2,-2,-1);
v:array[1..8] of -2..2 =(2,1,-1,-2,-2,-1,1,2);
type oo=record
x,y:shortint;
end;
var a:array[-1..50,-1..50] of oo;
{ ô a[i,j] =(0,0) nếu không có mìn và
n,kq:byte;
chưa ñược ñi qua, (x,y) nếu từ ô (x,y)
q:array[1..2500] of oo;
ngựa nhảy tới ô (i,j) và (100,100)
first,last:integer;
nếu có mìn }
xp,fn:oo;
procedure nhap;
var f:text;

tg:oo;
i,k,j,t:integer;
begin
Trang 11/17


***Ngăn xếp và hàng ñợi***
kq:=0;
tg.x:=100;
tg.y:=100;
assign(f,fi);
reset(f);
readln(f,n);
for i:=1 to n do
begin
read(f,k);
for j:=1 to k do
begin
read(f,t);
a[i,t]:=tg;
end;
end;
read(f,xp.x,xp.y);
a[xp.x,xp.y]:=tg;
read(f,fn.x,fn.y);
close(f);
last:=0;
first:=1;

S


x

x

end;
function empty:boolean;
x
ð
begin
empty:= first>last;
ð x
x
end;
procedure push(m:oo);
F
x
begin
last:=(last+1)mod 2500;
q[last]:=m;
(1,1)->(2,3)->(3,1)->(4,3)
end;
procedure pop(var x:oo);
begin
if not empty then
begin
x:=q[first];
first:=(first+1) mod 2500;
end;
end;

function inside(o:oo):boolean;
begin
inside:=(1<=o.x)and(o.x<=n)and(1<=o.y)and(o.y<=n);
end;
function ok(o:oo):boolean;

Trang 12/17


***Ngăn xếp và hàng ñợi***
begin
ok:=(a[o.x,o.y].x=0)and(a[o.x,o.y].y=0)and(inside(o));
end;
procedure xuat;
var o:oo;
begin
o:=fn;
kq:=1;
while (o.x<>xp.x)or(o.y<>xp.y) do
begin
write('(',o.x,',',o.y,') <-');
o:=a[o.x,o.y];
end;
write('(',o.x,',',o.y,')');
end;
procedure main;
var o,otg:oo;
i:integer;
b,dc:byte;
procedure restore;

begin
first:=first-1;
if first=0 then first:=2500;
a[q[first].x,q[first].y].x:=0;
a[q[first].x,q[first].y].y:=0;
first:=(first+1) mod 2500;
end;
begin
push(xp);
repeat
pop(o);
dc:=0;
inc(b);
for i:=1 to 8 do
begin
otg.x:=o.x+u[i];
otg.y:=o.y+v[i];
if ok(otg) then
begin
a[otg.x,otg.y]:=o;
if(otg.x=fn.x)and(otg.y=fn.y) then
begin
xuat;
break;

Trang 13/17


***Ngăn xếp và hàng ñợi***
end;

dc:=1;
push(otg);
end;
end;
if(i=8)and(dc=0) then
restore;
until empty;
if kq=0 then writeln('Can not');
end;
Begin
nhap;
main;
readln;
end.
3. Bài toán ñong nước :
Cho 2 bình dung tích lần lượt là m và n ( lít). Với nguồn nước không hạn chế, dùng 2 bình
trên ñể ñong k lít nước với kVí dụ : m = 5, n = 4, k =3
(0,0) -> (0,4) -> (4,0) -> ( 4,4) -> (5,3)
Chương trình :
Program donuoc;
type
trangthai = record
x,y,tt,truoc: integer;
end;
maxQ=0..1000;
mangTT=array[maxQ] of trangthai;
var
n, m, k: byte;
MO,DONG,Stack: mangtt;

topM,bottomM,topD,bottomD,top:maxQ;
xp:trangthai;
procedure sPush(var s:mangTT;var top:maxQ;u:trangthai);
begin
top:=top+1;
s[top]:=u;
end;
procedure sPop(s:mangtt;var top:maxQ;var u:trangthai);
begin
u:=s[top];
top:=top-1;

Trang 14/17


***Ngăn xếp và hàng ñợi***
end;
procedure qPush(var Q:mangTT;var top,bottom:maxQ;u:trangthai);
begin
top:=top+1;
Q[top]:=u;
if bottom=0 then bottom:=1;
end;
procedure qPop(Q:mangtt;var top,bottom:maxQ;var u:trangthai);
begin
u:=Q[bottom];
if bottom=top then begin bottom:=0;top:=0;end
else bottom:=bottom+1;
end;
function ktThuocHD(x,y:byte;top,bottom:maxq;Q:mangtt):boolean;

var i:maxQ;
begin
if bottom=0 then ktthuocHD:=false
else
begin
i:=bottom;
While (i<=top) and ((q[i].x<>x) or (q[i].y<>y )) do
i:=i+1;
ktthuochd:=(i<=top);
end;
end;
procedure ChuyenTT(u:trangthai;var v:trangthai;i:byte);
begin
case i of
1: begin v.x:=n; v.y:=u.y;end;
2: begin v.x:=u.x; v.y:=m; end;
3: begin v.x:=0; v.y:=u.y;end;
4: begin v.x:=u.x; v.y:=0;end;
5: if (u.x+u.y <= m) then begin v.x:=0; v.y:=u.x+u.y;end
else begin v.x:= u.x+u.y-m; v.y:=m;end;
6: if u.x+u.y <= n then begin v.x:=u.x+u.y; v.y:=0; end
else begin v.y:=u.x+u.y-n; v.x:=n;end
end;
end;
Procedure inkq;
var i,j:maxq;u:trangthai;
begin

Trang 15/17



***Ngăn xếp và hàng ñợi***

{

i:=topd;
while dong[i].tt<>0 do
begin
j:=dong[i].truoc;
spush(mo,top,dong[i]);
while dong[i].tt<>j do i:=i-1;
end;
spush(mo,top,dong[i]);}

while Top<>1 do
begin
spop(mo,top,u);
write('(',u.x,',',u.y,') -->');
end;
spop(mo,top,u);
writeln('(',u.x,',',u.y,')');
end;
procedure bfs(u: trangthai);
var
n,m:trangthai;
tt:maxq;i:byte;
begin
qPush(MO,topm,bottomM,u);
tt:=1;
while bottomM<>0 do

begin
qPop(Mo,topm,bottomM,n);
qPush(Dong,topd,bottomd,n);
for i:=1 to 6 do
begin
chuyenTT(n,m,i);
if (not ktthuochd(m.x,m.y,topm,bottomM,Mo) ) and
(not ktthuochd(m.x,m.y,topd,bottomd,dong) ) then
begin
tt:=tt+1;
m.tt:=tt;
m.truoc:=n.tt;
qPush(Mo,topm,bottomM,m);
if (m.x=k) or (m.y=k) then
begin qpush(dong,topd,bottomd,m);inkq; exit; end;
end;
end;
end;
writeln('Khong thanh cong');

Trang 16/17


***Ngăn xếp và hàng ñợi***
end;

procedure KhoiTao;
begin
write('Nhap n, m va k:');
readln(n,m,k);

xp.x:=0;xp.y:=0;xp.tt:=1;xp.truoc:=0;
topm:=0;bottomm:=0;topd:=0;bottomd:=0;
top:=0
end;
BEGIN
khoitao;
bfs(xp);
readln;
END.
Bài tập áp dụng :
1. Bài toán mã ñi tuần : Cho bàn cờ n x n. Con mã ñứng tại ô (x0,y0), hãy tìm một ñường
ñi ñể con mã ñi qua tất cả các ô bàn cờ, mỗi ô một lần.

Trang 17/17



×