Download as pptx, pdf, or txt
Download as pptx, pdf, or txt
You are on page 1of 52

Chương 2:

Các phương pháp duyệt đồ thị

Môn học: Lý thuyết đồ thị

PGS.TS. Dương Tuấn Anh

1
Nội dung

 1. Duyệt theo bề rộng trước (BFS)


 2. Duyệt theo chiều sâu trước (DFS)
 3. Một số ứng dụng của DFS:
 Phát hiện chu trình trong đồ thị vô hướng
 Nhận diện các thành phần liên thông trong đồ thị
vô hướng
 4. Xếp thứ tự tô pô
 Phương pháp 1
 Phương pháp 2

2
Duyệt đồ thị là gì?
 Duyệt hay tìm kiếm trên đồ thị là viếng mỗi đỉnh/nút trong
đồ thị một cách có hệ thống.
 Có hai cách chính để duyệt đồ thị: duyệt theo chiều sâu
trước (depth-first-traversal) và duyệt theo chiều rộng
trước (breadth-first-traversal).
 Duyệt theo chiều rộng trước sử dụng hàng đợi như là cấu
trúc dữ liệu hỗ trợ và duyệt theo chiều sâu trước sử dụng
stack như là cấu trúc dữ liệu hỗ trợ.
 Stack là cấu trúc dữ liệu dạng LIFO (Last In First Out) và cho
phép chúng ta thực hiện nhanh các thao tác Push, Pop.
 Queue là cấu trúc dữ liệu dạng FIFO (First In First Out) và cho
phép chúng ta thực hiện nhanh các thao tác Enqueue,
Dequeue

3
1. Duyệt theo chiều rộng trước
Khi duyệt đồ thị nếu ta dùng một queue như một cấu trúc dữ
liệu hỗ trợ, ta sẽ đi đến một giải thuật duyệt theo chiều rộng
trước (breadth-first-search).
procedure bfs;
procedure visit(n: vertex);
begin
đưa đỉnh n vào hàng đợi;
while hàng đợi khác rỗng do
lấy một đỉnh ở đầu hàng đợi, xử lý nó và thêm bất kỳ đỉnh nào
kế cận với đỉnh này mà chưa được xử lý vào hàng đợi và
đổi trạng thái của chúng sang trạng thái “sẵn sàng”.
end;
begin
Khởi tạo trạng thái của mọi đỉnh trong đồ thị;
for mỗi đỉnh n trong đồ thị do
if trạng thái của n là “chưa được viếng” then visit(n)
end;
4
Giải thuật duyệt theo chiều rộng trước
Giả sử chúng ta biểu diễn đồ thị vô hướng bằng một tập
danh sách kế cận. Mã giả chi tiết của giải thuật duyệt đồ thị
theo chiều rộng trước (list-bfs) được cho như sau:
 
procedure list-bfs;
var id, k: integer; val: array[1..maxV] of integer;
begin
id := 0; queue-initialze;
for k := 1 to V do val[k]: = 0; /* khởi tạo trạng thái của mọi đỉnh */
edges := null; /* khởi tạo tập cạnh sẽ được duyệt */
for k := 1 to V do
if val[k] = 0 then visit(k);
output edges
end;
Ghi chú: Tập edges dùng để lưu những cạnh được duyệt qua
trong quá trình BFS.
5
procedure visit(k: integer);
var t: link;
begin
put(k); /* đưa đỉnh k vào hàng đợi */
repeat
k := get; /* lấy ra một đỉnh từ đầu hàng đợi */
id := id + 1; val[k] := id; /* chuyển trạng thái của đỉnh k sang
“đã viếng” */
t := adj[k]; /* tìm các đỉnh kế cận với đỉnh k, t là biến chạy */
while t <> z do /* z là nút rỗng */
begin
if val[t .v] = 0 then
begin
edges := edges  {(k, t .v)} /* lưu cạnh (k, t .v ) vào tập edges
put(t.v); val[t.v]: = -1 /* chuyển đỉnh t.v sang trạng thái
“sẵn sàng” */
end;
t := t.next
end
until queueempty
end; 6
Ghi chú: Mảng val[1..V] chứa trạng thái của cácđỉnh.
val[k] = 0 nếu đỉnh k chưa hề được viếng (“not yet visited”),
val[k] ≠ 0 nếu đỉnh k đã được viếng.
val[k]= j nghĩa là đỉnh jth mà được viếng trong quá trình
duyệt là đỉnh k.
val[k] = -1 nghĩa là đỉnh k đã đang ở trong hàng đợi (tức là
trạng thái sẵn sàng).

Lưu ý: thứ tự của các đỉnh trong các danh


sách kế cận có ảnh hưởng đến thứ tự
duyệt của các đỉnh khi áp dụng BFS.

7
A: F-C-B-G B: A C: A G: A-E F: E-D

E: F-D-G D: F-E

D D D D D

E E E E

G G G G

B B B M M M
edges = {AF, AC, AB, AG,
C C L L FE, FD, HI, JK, JL, JM}
F K

A J

Hình 2.1 Nội dung của hàng đợi khi thực thi BFS cho đồ thị bên cạnh

Kết quả của BFS: AFCB GE D HI JKLM 8


Rừng BFS

Kết quả của BFS: AFCBGED HI


JKLM
Từ tập cạnh lưu trong tập edges, xây
dựng nên rừng BFS như trong hình 2.2.
Rừng BFS gồm 3 cây cho thấy đồ thị
được cho gồm 3 thành phần liên thông.

Hình 2.2 Rừng BFS


9
2. Duyệt theo chiều sâu trước

Duyệt đồ thị theo chiều sâu trước


Có hai giải thuật duyệt theo chiều sâu trước (depth-first-
search):
- giải thuật lặp (không đệ quy) và
- giải thuật đệ quy.
Hai giải thuật này hoàn toàn tương đương với nhau
Giải thuật lặp (không đệ quy) có sử dụng cấu trúc dữ liệu
stack một cách tường minh.
Giải thuật đệ quy sử dụng cấu trúc dữ liệu stack một cách
tiềm ẩn.

10
Giải thuật duyệt theo chiều sâu trước không đệ quy
procedure visit(n:vertex);
begin
đưa đỉnh n vào stack;
while stack còn chưa rỗng do
lấy một đỉnh ra khỏi stack, xử lý đỉnh này,
và đưa mọi đỉnh kế cận của n mà chưa hề được xử lý vào stack.
if một đỉnh đã có trong stack rồi, thì không cần đưa nó vào stack
nhưng chuyển nó lên vị trí đỉnh stack.
end;
procedure dfs;
begin
Khởi tạo trạng thái của mọi đỉnh trong đồ thị;
for mỗi đỉnh n trong đồ thị do
if trạng thái của n là “chưa được viếng” then visit(n)
end;

11
Mã giả giải thuật dfs (không đệ quy)
Giả sử chúng ta biểu diễn đồ thị vô hướng bằng một tập danh sách kế
cận. Mã giả chi tiết của giải thuật duyệt đồ thị theo chiều sâu trước
(không đệ quy) được cho như sau: 
procedure list-dfs;
var id, k: integer; val: array[1..maxV] of integer;
p:array[1..maxV] of integer; /* mảng dùng để suy ra tập edges */
begin
id := 0;
for k := 1 to V do /* khởi tạo trạng thái của mọi đỉnh */
begin val[k]: = 0; p[k]: = nil end;
for k := 1 to V do
if val[k] = 0 then visit(k);
edges := null; /* xác định tập cạnh được duyệt */
for k := 1 to V do
if p[k] <> nil then edges := edges  {(p[k], k)};
output edges
end;
12
procedure visit(k: integer);
var t: link;
begin
push(k); /* đưa đỉnh k vào stack */
repeat
k := pop; /* lấy ra một đỉnh từ stack */
id := id + 1; val[k] := id; /* chuyển trạng thái của đỉnh k sang “đã viếng” */
t := adj[k]; /* tìm các đỉnh kế cận với đỉnh k, t là biến chạy */
while t <> z do
begin
if val[t .v] = 0 then
begin
push(t.v); val[t.v]: = -1 /* chuyển đỉnh t.v sang trạng thái “sẵn sàng” */
p[t.v] := k /* ghi nhận cạnh (k, t .v)
end
else if val[t .v] = -1 then
begin shift(t.v); /*chuyển đỉnh t.v lên top của stack*/
p[t.v] := k end;
t := t.next /* chuyển qua đỉnh kế tiếp trong danh sách kế cận */
end
until stackempty
end;
13
Ghi chú: Mảng val[1..V] chứa trạng thái của cácđỉnh.
val[k] = 0 nếu đỉnh k chưa hề được viếng (“not yet visited”),
val[k] ≠ 0 nếu đỉnh k đã được viếng.
val[k]= j nghĩa là đỉnh jth mà được viếng trong quá trình duyệt là
đỉnh k.
val[k] = -1 nghĩa là đỉnh k đã đang ở trong stack (tức là trạng thái
sẵn sàng).

Khi đỉnh kế cận của đỉnh k đang xét đã được đưa vào stack thì
không cần đưa đỉnh này vào stack, nhưng phải dịch chuyển nó lên
vị trí đỉnh của stack (tác vụ shift)

Lưu ý: thứ tự của các đỉnh trong các danh


sách kế cận có ảnh hưởng đến thứ tự
duyệt của các đỉnh khi áp dụng DFS.

14
Thí dụ 1: DFS trên đồ thị vô hướng

Hình 2.3. Đồ thị vô hướng

Mảng p:
p(G) = A, p(C) = A,
p(B) = A, p(F) = A
p(E) = F, p(D) = F
p(G) = E, p(D) = E

 
Chú ý: Có sự dịch chuyển G, D lên vị trị đỉnh của stack.
Thứ tự duyệt: A – F – E – G – D – B – C
Tập edges = { AC, AB, AF, FE, EG, ED}
15
Cây DFS
Thứ tự duyệt: A – F – E – G – D – B – C
Tập edges = { AC, AB, AF, FE, EG, ED}

Lưu ý: Có những cạnh có trong đồ thị nhưng


không xuất hiện trong rừng hay cây DFS.
Những cạnh có xuất hiện trong rừng hay cây
DFS được gọi là những cạnh tiến (forward
edge) và những cạnh không xuất hiện được
Hình 2.4 Cây DFS gọi là những cạnh lùi (back edge). Cạnh lùi
GA cho thấy A-F-E-G-A là một chu trình.
16
Tìm kiếm theo chiều sâu trước – biểu diễn bằng
tập danh sách kề (giải thuật đệ quy)
procedure visit (k: integer);
var t: link;
begin
id := id + 1; val[k] := id; /* chuyển trạng thái của đỉnh k sang “đã
viếng” */
t := adj[k]; / * tìm các đỉnh kế cận của đỉnh k , t là biến chạy */
while t <> z do /* z là nút rỗng */
begin
if val[t .v] = 0 then
begin
edges := edges  {(k, t .v)};
visit(t.v) /* gọi đệ quy */
end;
t := t.next /* chuyển qua đỉnh kế tiếp trong danh sách kề */
end
end; 17
procedure list-dfs;
var id, k: integer;
val: array[1..maxV] of integer;
begin
id := 0;
for k := 1 to V do val[k] := 0; /* khởi tạo trạng thái của mọi đỉnh
đều là chưa viếng */
edges := null;
for k := 1 to V do
if val[k] = 0 then visit(k);
output edges
end;
Ghi chú: Mảng val[1..V] chứa trạng thái của cácđỉnh.
val[k] = 0 nếu đỉnh k chưa hề được viếng (“not yet visited”),
val[k] ≠ 0 nếu đỉnh k đã được viếng.
val[k]: = j nghĩa là đỉnh jth mà được viếng trong quá trình duyệt là
đỉnh k.
18
Giải thuật sử dụng một thủ tục đệ quy visit để viếng tất cả các
đình thuộc về cùng một thành phần liên thông với đỉnh nêu ở
đối số. Khi viếng một đỉnh, chúng ta sẽ xét tất cả các cạnh nối
từ đỉnh đó để xem chúng có dẫn đến những đỉnh nào chưa hề
được viếng, nếu có thì chúng ta sẽ viếng những đỉnh đó.
Như vậy kết quả của DFS trên đồ thị cho ở hình 2.3 là
A- F- E –G- D -B -C

19
Diễn tiến quá trình gọi đệ quy A: F-G-B-C
B: A
C: A
G: A-E
F: A-E
E: F-G-D
D: E-F

visit (A)  visit(F)  visit(E)  visit(G)  hoàn tất lượt gọi đệ quy tại
đỉnh G, quay lui về đỉnh E  visit(D)  hoàn tất lượt gọi đệ quy tại
đỉnh D, quay lui về đỉnh E  hoàn tất lượt gọi đệ quy tại đỉnh E, quay
lui về đỉnh F  hoàn tất lượt gọi đệ quy tại đỉnh F, quay lui về đỉnh A
 visit(B)  hoàn tất lượt gọi đệ quy tại đỉnh B, quay lui về đỉnh A 
visit(C)  hoàn tất lượt gọi đệ quy tại đỉnh C, quay lui về đỉnh A 
hoàn tất A, kết thúc .
Tập edges = {AF, FE, EG, ED, AB, AC} 20
Thí dụ 2: DFS trên đồ thị có hướng (dùng giải
thuật lặp)
A: B
B: D-C
C: A
D: A-C
E: F-G
F: B
G: F-D

Hình 2.5 Đồ thị có hướng Diễn tiến của stack:


gồm nhiều thành phần
liên thông mạnh
C G
A B D D E F F
Thứ tự duyệt: A-B-C-D E-G-F
p(B) = A, p(D) = B, p(C)= B, p(F)= E, p(G) = E
Tập edges = {AB, BD, BC, EF, EG}
21
Thí dụ 2 (tt.)
Tập edges = {AB, BD, BC, EF, EG}

Hình 2.6 Rừng DFS của đồ thị có hướng ở hình 2.5

Rừng DFS có những cạnh lùi như CA, DA. Cạnh CA cho thấy A-
B-C là một chu trình và và cạnh DA cho thấy A-B-D cũng là một
chu trình

22
Độ phức tạp của DFS

 Tính chất 3.1.1 Duyệt theo chiều sâu trước một đồ


thị biểu diễn bằng các danh sách kế cận đòi hỏi thời
gian tỉ lệ V+ E.

 Chứng minh: Chúng ta phải gán trị cho mỗi phần tử


của mảng val (do đó tỉ lệ với O(V)), và xét mỗi nút
trong các danh sách kết cận biểu diễn đồ thị (do đó
tỉ lệ với O(E)).

23
DFS đệ quy– biểu diễn bằng ma trận kế cận
Cùng một phương pháp có thể được áp dụng cho đồ thị được
biểu diễn bằng ma trận kế cận bằng cách dùng thủ tục visit
sau đây:

procedure visit(k: integer);


var t: integer;
begin
id: = id + 1; val[k]: = id;
for t: = 1 to V do
if a[k, t] then
if val[t] = 0 then
begin
edges := edges  {(k,t)};
visit(t)
end
end;
24
3. Ứng dụng của giải thuật duyệt theo
chiều sâu trước
Trong phần này, ta xem xét hai ứng dụng của duyệt theo
chiều sâu trước:
 Nhận diện chu trình trong đồ thị vô hướng
 Nhận diện các thành phần liên thông trong đồ thị vô
hướng

25
Nhận diện chu trình trong đồ thị vô hướng

Cho một đồ thị vô hướng G và ta muốn kiểm


tra xem G có chứa chu trình hay không. Giải
thuật duyệt theo chiều sâu trước có thể được
dùng để giải quyết vấn đề này. Nếu một cạnh
lùi (backward edge) xuất hiện trong quá trình
duyệt theo chiều sâu trước, thì chứng tỏ G có
chứa chu trình.

Giả sử G là đồ thị có chu trình. Nếu ta duyệt G


theo chiều sâu trước thì sẽ có một đỉnh v nào
đó trong chu trình có thứ tự duyệt thấp nhất.
Nếu (u, v) là một cạnh trong chu trình và u có
thứ tự duyệt lại cao hơn v ít nhất là 2 đơn vị, thì
u phải là hậu duệ của v. Như vậy cạnh (u,v) là
cạnh lùi, như minh họa ở hình bên.
26
Nhận diện chu trình trong đồ thị vô hướng (tt.)

Ta có thể cải biên thủ tục visit trong giải thuật DFS đệ quy để giải thuật
DFS trở thành giải thuật nhận diện chu trình trong đồ thị vô hướng. Thủ
tục visit được cải biên thành thủ tục có tên cycledetection như sau:
procedure cycledetection(k: integer);
var t: link;
begin
id := id + 1; val[k] := id; /* chuyển trạng thái của đỉnh k sang “đã viếng” */
t := adj[k]; / * duyệt các đỉnh kế cận của đỉnh k, dùng biến chạy t */
while t <> z do /* z là nút rỗng */
begin
if val[t .v] = 0 then cycledetection(t.v)
else if val[t .v] <= val[k] - 2 then thông báo phát hiện một chu trình ;
t := t.next /* dịch chuyển sang đỉnh kế tiếp trong danh sách kề */
end
end;

27
Chương trình chính:

for k := 1 to V do val[k]:= 0;
cycledetection(s) /* chọn một đỉnh s nào đó làm đỉnh xuất phát */

Thí dụ

call G
call E call E
call F call F call F
call A call A call A call A
Main Main Main Main

Phát hiện chu trình khi xử lý đỉnh G


val[“A”] =1, val[“F”] =2, val[“E”] =3,
val[“G”] =4. A là đỉnh kế cận của G nhưng
val[“G”] – val[“A”] > 2. Đã tìm thấy một chu
trình
28
Nhận diện các thành phần liên thông trong đồ thị vô hướng

Ta có thể cải biên giải thuật DFS để nhận dạng các thành phần liên
thông của một đồ thị vô hướng như sau:
procedure visit (k: integer);
var t: link;
begin
id := id + 1; val[k] := id;
write(name(k)); /* print out the name of the vertex which has
been visited */
t := adj[k]; /* duyệt danh sách kế cận của đỉnh k, dùng biến chạy t */

while t <> z do
begin
if val[t.v] = 0 then visit(t.v);
t := t.next /* chuyển sang nút kế tiếp trong danh sách kề */
end
end;
29
begin
id := 0;
for k := 1 to V do val[k] := 0;
write(‘The first connected component:’)
for k := 1 to V do
begin
if val[k] = 0 then visit(k);
writeln; writeln; write(‘Another connected component:’)
// move to new line,
// prepare to print out the list of vertices belonging to a
// new connected component
end;
end;

Ghi chú: Số cây trong rừng DFS bằng với số thành phần liên thông
của đồ thị
30
Thí dụ

Kết qủa:
The first connected component: A F E G D B C

Another connected component: H I

Another connected component :J L M K


31
4. Xếp thứ tự tôpô
Các đồ thị có hướng là các đồ thị trong đó các cạnh nối với
các nút có hướng.

H I
B C G

D E J K

L M
Hình 2.7. Một thí dụ về đồ thị có hướng

32
Thường thì hướng của các cạnh biểu thị mối liên hệ trước
sau (precedence relationship) trong ứng dụng được mô hình
hóa.

Thí dụ, đồ thị có hướng có thể được dùng để mô hình hóa


một đường dây sản xuất (assembly line).

Trong phần này, chúng ta xem xét giải thuật sắp thứ tự topo
(topological sorting)

33
Xếp thứ tự tôpô
Đồ thị có hướng không chu trình (Directed Acyclic Graph)
Đồ thị có hướng mà không có chu trình được gọi là các đồ
thị có hướng không chu trình (dags).
Tập thứ tự riêng phần và xếp thứ tự tôpô
Cho G là một đồ thị có hướng không chu trình. Xét quan
hệ thứ tự < được định nghĩa giữa các đỉnh của đồ thị như
sau:
u < v nếu có một lối đi từ u đến v trong G.

Quan hệ này có 3 tính chất:


(1) Với mỗi đỉnh trong V[G], not (u < u). (không phản xạ)
(2) nếu u < v, thì not( v < u) . (không đối xứng)
(3) nếu u < v và v < w, thì u < w. (Truyền)
Quan hệ < là một quan hệ thứ tự riêng phần.
34
Xếp thứ tự tôpô

Cho G là một đồ thị có hướng không chu trình. Một


thứ tự tôpô (topological sort)T của G là một thứ tự
tuyến tính mà bảo toàn thứ tự riêng phần ban đầu
trong tập đỉnh V[G].

Nghĩa là: nếu u < v trong V (tức là nếu có một lối đi


từ u đến v trong G), thì u xuất hiện trước v trong thứ
tự tuyến tính T.

35
A

H I
B C G

D E J K

L M

Các nút trong đồ thị ở hình trên có thể được sắp thứ tự tôpô
theo thứ tự sau:
J K L M A G H I F E D B C

36
Phương pháp 1 sắp xếp tôpô
Phương pháp này thực hiện theo kiểu tìm kiếm theo chiều sâu trước :
thêm một nút vào danh sách mỗi khi cần thiết lấy một nút ra khỏi
stack để tiếp tục. Khi gặp một nút không có nút đi sau thì ta sẽ lấy ra
(pop) một phần tử từ đỉnh stack. Lặp lại quá trình này cho đến khi
stack rỗng. Đảo ngược danh sách này ta sẽ được thứ tự tôpô.
Giải thuật:
1. Nhận diện các đỉnh không có đỉnh đi trước (tức các đỉnh nguồn),
đưa chúng vào stack.
2. while stack khác rỗng do
if phần tử đang ở đỉnh stack là một đỉnh có một số đỉnh kế cận
then đưa tất cả những đỉnh kế cận này vào stack
else lấy phần tử đang ở đỉnh stack ra khỏi stack, gỡ đỉnh tương
ứng ra khỏi đồ thị và đưa vào danh sách kết quả.
3. Đảo ngược danh sách kết quả để được thứ tự tô pô.
.

37
Hình 2.8 Sắp thứ tự
tôpô cho đồ thị có
hướng bằng phương
pháp 1

List: 8- 5- 3- 6- 4- 10- 2- 1- 9- 7
Kết quả: 7- 9- 1- 2- 10- 4- 6- 3- 5- 8
38
Với một đồ thị có hướng không chu trình, được biểu diễn bằng tập
danh sách kế cận và duy trì một mảng indeg và một mảng outdeg
mà với đỉnh k, indeg[k] = bậc vào và outdeg[k] = bậc ra đỉnh k. Giải
thuật sắp thứ tự tô pô theo phương pháp thứ nhất được chi tiết
hóa như sau:
procedure toposort1;
var k: integer;
begin
1 for k := 1 to V do
2 if indeg[k] = 0 then push(k); /* đưa các đỉnh nguồn vào stack */
3 repeat
4 k1 := top(stack); /* top(stack) là phần tử đang ở đỉnh stack */
if outdeg[k1]= 0 then /* nếu đỉnh k1 là đỉnh tận cùng */
begin
5 k:= pop; /* lấy phần tử đanh ở đỉnh stack ra khỏi stack */
output k to the output-list /* đưa k vào danh sách kết quả */
giảm outdeg (bậc ra) của những đỉnh đi vào đỉnh k
end
39
else /* đỉnh k1 đang xét không phải là đỉnh tận cùng */
begin
t := adj[k1]; /* t là biến chạy dùng để duyệt qua danh sách
kế cận của đỉnh k1 */
while t <> z do /* z là nút rỗng */
begin
8 push(t.v); /* đưa đỉnh kế cận với đỉnh k1 vào stack */
9 t := t.next;
end
end
10 until stackempty;
11 đảo ngược output-list để được thứ tự tô pô
end;

40
Độ phức tạp của giải thuật sắp xếp tô pô
phương pháp 1
 Tính chất: Độ phức tạp tính toán của giải thuật sắp
xếp tô pô là O(|E|+|V|) nếu đồ thị được diễn tả bằng
tập danh sách kế cận.

 Chứng minh: Điều này hiển nhiên vì thân của vòng


lặp while được thực hiện tối đa 1 lần cho mỗi cạnh.
Và tác vụ khởi tạo stack thì tỉ lệ với số đỉnh của đồ
thị (O(V)).

 Ghi chú: Nhờ indegree của mỗi đỉnh, ta có thể nhận


diện ra các đỉnh nguồn của đồ thị.

41
Phương pháp 2 sắp thứ tự tô pô

 Ý tưởng: Liên tiếp nhận diện một nút là nút nguồn


(nút không có nút đi trước) và tháo gỡ nó ra khỏi đồ
thị cùng với các cạnh đi ra từ nó. Quá trình lặp sẽ
dừng lại khi không còn nút trong đồ thị. Thứ tự của
các nút bị xóa bỏ sẽ tạo thành một lời giải của bài
toán sắp thứ tự tô pô.

42
Giải thuật của phương pháp 2
Giải thuật của phương pháp 2 để xếp thứ tự tô pô như sau:

Nhận diện những đỉnh không có đỉnh đi trước, đưa chúng vào hàng
đợi.
while hàng đợi khác rỗng do
Lấy đỉnh N ở đầu hàng đợi ra
for mỗi đỉnh M kế cận với đỉnh N do
xóa bỏ cạnh nối từ N đến M
if nếu đỉnh M không còn có đỉnh đi trước then
đưa đỉnh M vào đuôi hàng đợi
endfor
endwhile

43
Hình 2.9

Thứ tự tô pô là a, b, c, d, e

Trạng thái của hàng đợi 44


Với một đồ thị có hướng không chu trình, được biểu diễn bằng tập
danh sách kế cận và duy trì một mảng indeg mà với đỉnh k,
indeg[k] = số cung đi vào đỉnh k.
Giải thuật sắp thứ tự tô pô theo phương pháp thứ hai được chi tiết
hóa như sau:

procedure toposort2;
var k: integer;
begin
queueinitialize; /* khởi tạo hành đợi */
1 for k := 1 to V do
2 if indeg[k] = 0 then put(k); /* đưa các đỉnh nguồn vào hàng đợi */
3 repeat
4 k := get; /* lấy phần tử ở đầu hàng đợi ra để xét */
5 output k to the output-list ;
6 t := adj[k]; /* duyệt danh sách kế cận của đỉnh k */

45
7 while t <> z do /* z là nút rỗng */
begin
8 indeg[t.v] = indeg[t.v] -1; /* xóa bỏ cạnh (k, t.v) ra khỏi
đồ thị */
9 if (indeg[t.v] = 0) then put(t.v); /* nếu đỉnh t.v trở thành
đỉnh nguồn thì đưa đỉnh này vào hàng đợi */
10 t := t.next;
end
11 until queueempty;
end;

46
Kết luận
 Duyệt theo chiều sâu trước và duyệt theo bề rộng trước
là hai giải thuật duyệt đồ thị quan trọng.
 Bằng cách biểu diễn một đồ thị dưới hình thức cây DFS
hay cây BFS, chúng ta có thể tìm ra nhiều đặc tính của
đồ thị như tính liên thông, có chứa chu trình hay không.
 Hai giải thuật này có độ phức tạp tính toán tương đồng.
 Xếp thứ tự tô pô là một bài toán dùng với đồ thị có
hướng không có chu trình và có ứng dụng trong các ứng
dụng xếp lịch. Có thể xếp thứ tự tô pô dựa vào một trong
hai phương pháp: phương pháp dựa vào duyệt theo
chiều sâu trước và phương pháp dựa vào duyệt theo bề
rộng trước

47
Phụ Lục A
Diễn tiến cấp phát vùng bộ nhớ khi
thực thi giải thuật DFS đệ quy

Môn học: Lý thuyết đồ thị

PGS.TS. Dương Tuấn Anh

48
Diễn tiến cấp phát vùng bộ nhớ khi thực thi giải thuật
DFS đệ quy

 Ứng với đồ thị vô hướng ở hình trên (bên trái), diễn tiến
cấp phát các vùng bộ nhớ hoạt động (activation
record) cho các trình con được gọi khi thực thi giải thuật
DFS đệ quy được minh họa ở slide kế tiếp.
49
Các vùng bộ nhớ của
các trình con được
hệ thống cấp phát
xếp chồng lên nhau
theo cơ chế cấu trúc
stack.

Tập edges ={ AF, FE,


EG, ED, AB, AC}.
val[“A”]=1,
val[“F”]=2,
val[“E”]=3,
val[“G”]=4,
Val[“D’]=5
val[“B”]=6,
val[“C”]=7,
50
Trạng thái của stack do hệ thống cấp phát cho giải thuật
DFS đệ quy để duyệt đồ thị nêu trên có thể diễn tả lại một
cách gọn gàng như sau:

Tập edges ={ AF, FE, EG, ED, AB, AC}.

51
Cây DFS

52

You might also like