Chương 6: Đồ thị - Graph | Lý thuyết môn Cấu trúc dữ liệu và giải thuật | Đại học Xây dựng Hà Nội

Cấu trúc dữ liệu đồ thị (graph) là kiểu cấu trúc tổng quát hơn cấu trúc cây TREE đã học. Sơ đồ mạng lưới (network) là một dạng ứng dụng biến thể của đồ thị.Một đồ thị ở dạng biểu diễn hình ảnh là các cặp nút và cạnh nối với nhau. Tài liệu giúp bạn ôn tập tham khảo và đạt kết quả cao. Mời bạn đọc đón xem!

lOMoARcPSD| 45148588
Bài 6 Chương 4. ĐỒ THỊ - GRAPH
4.1.Khái niệm
Cấu trúc dữ liệu ồ thị (graph) là kiểu cấu trúc tổng quát hơn cấu trúc cây TREE ã học. Sơ ồ mạng
lưới (network) là một dạng ứng dụng biến thể của ồ thị.
Một ồ thị ở dạng biểu diễn hình ảnh các cặp nút và cạnh nối với nhau.
Một ồ thị ở dạng biểu diễn toán học là một tập hợp (V, E), trong ó V(vertices) là tập các nút và
E(edges) là tập các cạnh nối các cặp nút.
Một ồ thị mà các cnh nối nút không có hướng gọi là ồ thị vô hướng, các cạnh có hướng gọi là ồ th
có hướng.
Ví dụ. trong ồ thị ở hình 1 dưới ây, tập cnút V= {A,B,C,D,E} , tập các cạnh E={AB, AC, BC,
BD,CD,DE}
- Nút kề nhau : 2 nút ược ni với nhau bằng 1 cạnh. Ví dụ A kề B, A kề C,.., D kE.
- Đường : biểu diễn một dãy các cạnh nối liền các nút. Trong hình trên, ACDE hoặc ABDE
biểu diễn 2 ường i từ A tới E.
4.2.Đồ thị có hướng
4.2.1.Khái niệm
Khi các nút nối với nhau bằng các cạnh hướng thì ta một thhướng. Kiểu cấu trúc cây
Tree ã xét trước ây là một dạng riêng của thì hướng, trong cấu trúc cây, các nút luôn xét theo
hướng i từ nút gốc ến các nút lá. Trong ồ thị trên, A là nút gốc, E là nút lá và có 3 ường có thể i từ A -
> E ABDE, ACDE, ABCDE. Khác với Cây, trong ồ thhướng, thể ở một nút hướng quay
vòng tại nút ó (giống hình ảnh một bùng binh, vòng xoay ở nút giao thông có nhiều ường giao nhau).
Đồ thị có hướng ược áp dụng nhiều trong thực tế, tạo thành một lĩnh vực riêng là sơ ồ mạng lưới, ược
vận dụng giải các bài toán như quản thi công các ng trình xây dựng, quản mạng ới iện, lập
kế hoạch sản xuất kinh doanh của các doanh nghiệp…
4.2.2.Cài ặt ồ thị có hưng
a)Ma trận kề - Adjacency matrix
Cách cài ặt thông dụng là dùng ma trận kề của một ồ thcó n nút, là mt ma trn vuông A có n hàng
n cột A[n][n], trong ó :
1 nếu có cạnh nối nút i ến nút j
A[i][j]
=
0 nếu không có cạnh nối nút i ến nút j
Hình 1.
Các khái ni
A
B
C
E
D
lOMoARcPSD| 45148588
2
A Adjacency matrix ma trận kề
Như vậy, với ồ thị hình 1 ta lập ma trận kề :
A B C D E
A 0 1 1 0 0
A[] = B 0 0 1 1 0
C 0 1 0 1 0
D 0 0 0 0 1
E 0 0 0 0 1
b)Mảng giá trị các nút
Dùng mảng D[] lưu dữ liệu các nút, D[i] là dữ liệu của nút i.
c)Danh sách kề
Trong ma trận kề thường có nhiều phần tử = 0, nếu lưu trữ tất cả sẽ lãng phí ô nhớ. Có cách khác lưu
trữ thông tin của ma trận kề tiết kiệm bnhớ bằng cách dùng danh sách liên kết theo hàng, mỗi danh
sách nút theo hàng sẽ một mảng liên kết, ược duy trì bởi một con trỏ trỏ ến nút ầu của mảng nút ó,
mỗi phần tử tiếp theo là một nút 2 thành phần : dliệu liên kết, ược nối tiếp ra từ nút ầu của
mảng nút.
Ký hiệu V[x] là con trỏ cho nút x.
V[A] -> ->
-
V[B] ->
V[C] -> ->
V[D] -> ->
V[E] ->
Nếu thay chữ cái tên nút bằng số thứ tự nút 1,2,.. thì ta danh sách kề có dạng :
V[1] -> ->
-
V[2] ->
V[3] -> ->
V[4] -> ->
E
NULL
5
NULL
A
next
->
B
next
->
C
next
->
D
next
B
next
->
C
next
->
D
NULL
->
E
NULL
C
next
D
NULL
->
E
NULL
E
NULL
D
next
E
NULL
1
next
->
2
next
->
3
next
->
4
next
2
next
->
3
next
->
4
NULL
->
5
NULL
3
next
4
NULL
->
5
NULL
5
NULL
4
next
A
B
C
E
D
lOMoARcPSD| 45148588
V[5] ->
Hình 2. Danh sách kề
Để thiết lập danh sách liền kề như hình 2 trên, có các bước :
1)Khai báo mảng các con trỏ V[], mỗi phần tử có 2 thành phần du_lieu và next -
n là số nút tối a của ồ th
2)Khởi tạo nút ầu tiên của nút i có dliệu, next ược gán NULL
{ V[i].du_lieu = du_lieu0; V[i].next = NULL}
Với mỗi nút i sẽ thêm các nút kế tiếp theo sơ ồ
như hình 2 cho ến hết, sử dụng hàm thêm nút vào cuối
danh sách liên kết ã học ở bài 2. 3)Tăng i và lặp lại
bước 2 cho ến khi i=n.
4.2.3.Duyệt tìm theo chiều sâu trên ồ thị có hướng
Ý tưởng của duyệt tìm theo chiều sâu
Hình 3 là từ gốc i xuống nút con, thăm nút con của
nút
con…cho ến khi hết các cấp dưới của nút con
mới quay lên duyệt nút con khác cho ến khi tìm ược.
Với ồ thị ở hình 3, ầu tiên thăm nút A, sau ó thăm nút
con B, thăm nút con E,
quay lên thăm nút con C, thăm tiếp các nút con của C là
F,G; quay lên thăm nút D, thăm tiếp nút con của D là H.
Thông thường dùng cấu trúc ngăn xếp – stack ể tổ chc
duyệt ồ ththeo chiều sâu.
5.2.4.Duyệt tìm theo chiều ngang trên ồ thị có hướng
Theo cách duyệt này ta i theo chiều ngang
của từng cấp.
Hình 4
Ví d: với ồ thị ở hình 4, ầu tiên i từ gốc A, sau
ó thăm các nút mức 1 có 3 nút B, C, D sau ó
thăm các nút mức 2 có 4 nút E, F, G, H.
Cách này giống như duyệt hết các nút ở mức thứ i ri mới duyệt ở mc thứ i+1 trong cây nhị phân.
Thông thường dùng cấu trúc hàng ợi – queue ể tổ chc duyệt ồ thị theo chiều ngang
4.3.Đồ thị vô hướng
Khi các cạnh trong ồ thị không chỉ hướng thì ta có ồ thị vô hướng. Trong ồ thị vô hướng không có
một nút nào ược nối với chính nó (không có xoay vòng).
5
NULL
A
H
B
D
C
F
G
E
A
H
B
D
C
F
G
E
lOMoARcPSD| 45148588
4
Cài ặt ồ thị vô hướng cũng gần giống như ối với ồ thị có hướng, dùng ma trận kề, danh sách các
cạnh.
4.3.1.Cài ặt ồ thị vô hướng bằng ma trận kề
Cho ồ thị như hình 5.
Ma trận kề bây giờ có dạng :
Hình 5
A B C D E F 2
C 1 0 0 1 0 2
D 0 1 1 0 1 2
E 0 0 0 1 0 2
A 0 1 1 0 0 0
A[] = B 1 0 0 1 0 0
F 0 0 1 1 1 0
Nhận xét : ma trận kề của ồ thị vô hướng luôn ối xứng qua ường chéo chính.
4.3.2.Cài ặt ồ thị vô hưng bằng danh sách các cạnh
Trong ồ thị vô hướng, mỗi nút ở ầu cạnh có thể nối vi nhiều nút khác. Chẳng hạn khi biểu
diễn mạng lưới các ường phố của một khu phố, có những oạn ường mà một ầu là ngã 6, một ầu
là ngã tư :
Danh sách các cạnh là khi ta ánh số th tự các nút, mỗi cạnh sẽ ược lưu trữ thông tin dạng CẠNH
NÚT (Edge Node), sẽ lưu nút V[i] mà cạnh ược ni tới. Trong hình 6 trên, cạnh nối A-B là cạnh nối
nút V[1] và V[2]; ồng thời phải hiểu V[1] là iểm cui ca cạnh khác và V[2] cũng vậy. Ta dùng
mảng các con trỏ V[i], mỗi con tr V[i] trỏ ến nút ầu lưu trữ dữ liệu của các nút, nút ầu V[i] này có
trường liên kết trỏ ến một cạnh- nút mà có nút i là một trong các iểm cui của nó.
Mỗi phần tử e
i
trong danh sách các cạnh có dạng :
V[1] e1 V[2]
A B
Nút 1 Nút 2
Hình 6
.
A
B
C
D
E
F
lOMoARcPSD| 45148588
LienKet[1] LienKet[2]
Khi cạnh với tính cách là 1 phần tử trong danh sách liên kết thì nó giống như 1 nút trong danh sách
liên kết ôi, có 2 con trỏ trái và phải liên kết ến nút bên trái và nút bên phi.
Với ồ thị ở hình 5 trên, ta có danh sách cạnh như sau:
1.AB
A
2.AC e1 e2
3.BD
B C
4.CD e4
5.CE e3 e5
6.DE
D
Hình 7
7.DF e6 e7
8.EF E e8
F
Chú ý : mt cạnh ã ược kê khai thì không kê khai lại, ví dụ ã khai báo cạnh AB thì không khai BA
nữa.
4.4.Các thuật toán cơ bản trên ồ thị
Trong lý thuyết ồ thị, có 2 kiểu duyt cơ bản là duyệt theo chiều ngang và duyệt theo chiều sâu.
Duyệt ồ thị theo chiều ngang (breadth-first searching BFS ) gần giống với duyt cây nhị phân theo
các mc trong cây, duyệt ồ thị theo chiều sâu(depth-first searching DFS) gần giống vi duyt cây
theo tiền tự DLR; Chỉ có chỗ khác khác biệt là trong ồ thị không có nút gốc cố ịnh, việc duyệt có th
bắt ầu từ một nút bất kỳ.
4.4.1.Thuật toán duyệtthị theo bề ngang – BFS
Ý ởng của thuật toán duyệt ththeo bề ngang duyệt c t của ththeo các mức theo
khoảng cách xa dần nút gốc(nút ược khởi ầu duyệt), mức sau cách mức trước 1 cạnh tính từ nút bắt
ầu.
Giải thích thuật ngữ : trong các thuật toán BFS và DFS có sử dụng cụm t “ ã duyệt”, một stài
liệu dùng với ý nghĩa không rõ ràng, dễ nhầm lẫn. Trong bài này nói “ ã duyệt” là ã có một tác ộng
vào nút ó, chẳng hạn ã ánh dấu “ ã duyệt”, ang thực hiện lập tập nút liền kề của nó, xét xem các nút
trong tập liền kề của nó ã ược duyệt chưa ể ưa vào hàng ợi…
4.4.1.1.Giới thiệu thuật toán
Xét bài toán : cho một thị vô hướng không có trọng số G=(V,E), trong ó V là tập các nút, E là tập
các cạnh; có một nút khởi ầu s V .
Để duyệt ồ thị theo BFS, sẽ sử dụng cấu trúc hàng ợi Q[] và danh sách chưa ược sắp là dãy bfs[] cài
t kiểu mảng array. Hàng ợi Q[] dùng cho việc thăm qua các nút, lưu các nút ã ược thăm và chờ ợi
duyệt, nút nào ược ánh dấu “ ã duyệt” sẽ xóa khỏi hàng i rồi sau ó mi thc hiện các thao tác liên
lOMoARcPSD| 45148588
6
quan ến nó. Mảng bfs[] ể lưu kết quả, chỉ số i của mảng bfs[i] là số th tự duyệt của nút , còn giá trị
của bfs[i] slà số hiệu nút, ví dụ bfs[6] = E có nghĩa là nút E có thứ tự duyệt là 6 . Khởi ầu, chưa
duyệt nút nào, gán tất ccác bfs[i] = 0, sau ó lần lượt các lần duyệt i=1,bfs[1] = <nút duyệt th
nhất>, i=2,bfs[2]= <nút duyệt thứ 2>,....
Thuật toán duyệt ồ thị theo bề ngang là duyệt các nút của ồ thị theo các mc theo khoảng cách xa
dần nút gốc(nút ược khởi ầu duyệt), mc thứ i+1 cách xa nút gốc hơn mức thi mt cạnh. Với ồ th
ở hình 8, mức 1 chỉ có nút khởi ầu A; mức 2 có nút B và C, mức 3 có nút D và F, mức 4 có một nút
E.
4.4.1.2.Các bước của thuật toán BFS
Đầu vào : ồ thị G=(V,E), V là tập các nút, E là tập các cạnh.
Đầu ra : thị G
kết quả
=(V,E), với iều kiện mỗi nút của ồ thị kết quược ni với nút khởi ầu s khi và
chỉ khi nó ã ược ánh dấu “ ã duyệt”.
1.Thiết lập hàng ợi Q[] và mảng bfs[], khi tạo nút s ưa vào Q;
Mảng bfs[] ược dùng ể lưu thứ tự duyệt các nút, bfs[i] = <số hiệu nút>, nghĩa là chỉ số i của dãy
bfs[i] là thứ tự duyệt các nút, ược ngm gán từ 1 ÷ n, n là số nút, còn giá trị của bfs[i] là tên nút
A/B/C/D/E/F, lúc ầu sẽ khởi tạo bfs[1]=…=bfs[n]=0. Ví dụ, bfs[1]=A ược hiểu là nút A duyệt ầu
tiên, hay bfs[1]= C nghĩa là nút duyệt ầu tiên là nút C. Trong ồ thị vô hướng, thuật toán duyệt theo
bề ngang BFS có thể bắt ầu duyệt từ một nút bất kỳ, nút ó ược lưu ở bfs[1] . Thuật toán BFS ược
khái quát như sau:
1.Nút s ược chọn khởi ầu duyệt, ký hiệu là u là nút ầu của mỗi bước lặp duyệt, nên bt ầu duyệt s
coi u=s và u ược ưa vào hàng ợi Q[]. Trong bfs[] chưa có nút nào ược duyệt; ký hiệu i là thứ tự duyệt
của 1 nút, k là thứ tự bước lặp thuật toán, lúc ầu gán i=0; k=0;
2.while (Q không rỗng)
{Lần lặp thứ k=k+1;
Kim tra nút u ã duyệt chưa, nếu bfs[i] = u là ã duyệt thì trở lại ầu vòng lặp while :
Nếu u chưa duyệt thì duyệt u, ánh dấu u ang duyệt, thứ tự duyệt i=i+1 => bfs[i]=u;
Cho m
ột ồ
th
như sau :
Hình 8
F
A
B
C
D
E
lOMoARcPSD| 45148588
A
B
C
Tạo danh sách các nút liền kề V{u}, kiểm tra ối vi mọi v ⸦ V{u} chưa duyệt thì ưa v vào Q[];
Nếu ã xét hết các nút liền kề trong V{u}, thì xóa u khi ầu hàng ợi Q[]. Kiểm tra nếu Q chưa
rỗng thì lặp lại while. }
Với ồ thị ở hình 8, từng bước thc hiện như sau.
Tạo dãy bfs[6] và hàngi Q[6]
Bước 0. Đưa nút khởi ầu A vào Q
Q
Tiếp theo thực hiện các bước lặp trong vòng lặp while cho ến khi hàng ợi Q[] rỗng :
while (!Empty(Q[]){Trong vòng lặp này thực hiện các lệnh :
Lấy ra nút ầu Q ặt là u (chưa xóa u ở hàng ợi) ; nút khởi ầu là u=A. Xóa u khỏi hàng ợi Q[];
Kiểm tra xem u ã duyệt chưa, nếu ã duyệt ( ã có bfs[i]=u) thì bỏ qua các phần sau, lặp lại
while
Duyệt u, ánh dấu u ang duyệt, thứ tự duyệt i=i+1 => bfs[i]=u;
Tạo tập các nút liền kề với u dưới dạng danh sách lienke(u);
Lần lượt xét từng nút v thuộc lienke(u), nếu v chưa ược duyệt (chưa có bfs[j]=v;j=1÷i) sẽ ưa
v vào Q[];
}
Lần lặp 1. k=k+1=1; Lấy nút A ầu hàng ợi, duyệt nút A,
Xóa nút A khỏi hàng ợi Q[];
Kiểm tra xem u ã duyệt chưa : nếu ã duyệt thì bỏ qua các phần sau, lặp lại while
ây các bfs[]=0 , chưa duyệt.
i=i+1, ghi nút Ao thứ tự duyệt i=1 vào bfs[1]=A,
i
1
bfs[i]
A
0
0
0
0
Tạo tập các nút liền kề với nút A: lienke(A)={B,C} (nút A ni với nút B , C). Lần
lượt xét 2 nút B,C này, chúng chưa ược duyệt vì chưa có mặt trong bfs[] nên ưa 2
nút này vào hàng ợi Q[], vẽ thêm nút B, C vào cây :
Q
Kiểm tra, Q chưa rỗng nên lặp tiếp vòng while.
Lần lặp 2. k=k+1=2. Lấy nút B ở ầu hàng ợi Q[]. Duyệt nút B, xóa nút B khỏi hàng ợi Q[];
Q
i
0
bfs[i]
A
B
C
C
i
1
2
bfs[i]
A
B
1
lOMoARcPSD| 45148588
8
Kiểm tra xem nút B ã duyệt chưa, nếu ã duyệt thì bỏ qua các phần sau, lặp
lại while.
Nhưng nút B chưa duyệt => Ghi nút B vào thứ tự duyệt i=i+1=2 => bfs[i]
=> bfs[2]= B;
Tạo tập các nút liền kề lienke(B)={D}, nút B ni với nút D, thêm nút D vào
cây : Xét nút D trong danh sách kề lienke(B), nó chưa ược duyệt nên ưa D
vào hàng ợi Q[]: Q
Kiểm tra, Q chưa rỗng nên lặp tiếp vòng while.
Lần lặp 3. k=k+1=3; Lấy nút C từ Q[]. Duyệt nút C, xóa nút C khỏi Q[]. Q
Kiểm tra xem u=C ã duyệt chưa, nếu ã duyệt thì bỏ qua các
phần sau, , lặp lại while, nhưng nút C chưa duyệt => i
=i+1=3, ghi bfs[i]= bfs[3]= nút C; Tạo tập các
nút liền kề lienke(C)={D,F}, nút C ni với nút
D,F; nút D ã có trong cây
nên chỉ thêm nút F vào cây:
Xét nút D,F trong danh sách kề lienke(C), chúng chưa ược duyt
nên ưa D,F vào Q[]: Q
Trên ồ thị dùng nét ứt nối C-D còn nút F bây giờ mới xét ến nên cạnh C-F v
nét liền. Đã xét tt c các nút v⸦ lienke(C).
Kiểm tra, Q chưa rỗng nên lặp tiếp vòng while
Lần lặp 4. k=k+1=4;Lấy nút D từ Q[]. Duyệt nút D, xóa nút D khỏi Q[]. Q
Kiểm tra xem u=D ã duyệt chưa, nếu ã duyệt thì bỏ qua phần
sau, lặp lại while,
Nhưng nút D chưa duyệt => i=i+1=4, ghi nút D vào
thứ tự duyệt i= 4 => bfs[4]=D;
Tạo tập các nút liền kề lienke(D)={B,C,E,F}, ch
xét nút E,F trong danh sách này vì chúng chưa ược duyt và ưa chúng vào Q[].
Cạnh D-E vẽ nét liền, còn D-F dùng nét ứt nối vì cạnh C-F ã nối liền từ nút C. Q
Đã xét tất cả các nút v⸦ lienke(D).
Kiểm tra, Q chưa rỗng nên lặp tiếp vòng while
Lần lặp 5. k=k+1=5; Lấy nút F từ Q[]. Duyệt nút F. xóa nút F khỏi Q[] : Q
Kiểm tra xem nút F ã duyệt chưa, nếu ã duyệt thì bỏ qua các phần
sau, lặp lại while. Nhưng nút F chưa duyệt => i=i+1=5;
Ghi nút F vào thứ tự duyệt i=5 =>bfs[5]= F;
C
D
D
i
1
2
3
bfs[i]
A
B
C
0
0
D
F
F
i
1
2
3
4
bfs[i]
A
B
C
D
F
E
F
E
F
i
1
2
3
4
5
fs[i]
A
B
C
D
F
E
F
E
A
B
C
D
C
A
B
D
F
E
C
A
B
D
F
lOMoARcPSD| 45148588
Tạo tập các nút liền kề lienke(F)={E,D,C}.
Xét các nút trong danh sách kề lienke(F),chỉ có nút E chưa duyệt nên ưa E
vào Q[].
Q
Nút F có cạnh nối F-E, F-D, F-C, nút C, D ã duyệt, nút E ã ược ni từ nút D,
nên cạnh F-E ta dùng nét ứt.
Đã xét tt ccác nút v⸦ lienke(F).
Kiểm tra, Q chưa rỗng nên lặp tiếp vòng while
Lần lặp 6 : k=k+1=6;Lấy nút E từ Q[]. Duyệt nútE5, xóa nút E khỏi Q[]. Q
Kiểm tra xem E ã duyệt chưa, nếu ã duyệt thì bỏ qua các phần
sau, lặp lại while,
Vì nút E chưa duyệt => i=i+1=6; Ghi nút E
vào thứ tự duyệt i=6 -> bfs[6]= E;
Tạo tập các nút liền kề lienke(E)={D,F}.
Xét tt cả các nút v⸦ lienke(E) ã duyệt hết nên không thêm vào Q[] Kiểm
tra, Q chưa Rỗng nên lặp tiếp vòng while.
Lần lặp 7 : k=k+1=7;Lấy nút F từ Q[]. xóa nút F khỏi Q[].
Q E
Kiểm tra nút F ã duyệt nên bỏ qua các phần sau, lặp lại while. Kiểm tra, Q
chưa Rỗng nên lặp vòng while
Lần lặp 8 : k=k+1=8;Lấy nút E từ Q[]. xóa nút E khỏi Q[]. Q
Kiểm tra nút E ã duyệt nên bỏ qua các phần sau, lặp lại while.
Kim tra, Q Rỗng nên kết thúc vòng while
Kết quả, dãy bfs có :
Thứ tự duyệt
1
2
3
4
5
6
Số hiệu nút
A
B
C
D
F
E
Ta có thể bắt ầu duyệt lại ồ thị theo bề rộng với một nút khác bất kỳ B/C/D/E/F.
Danh sách kết quả bfs[] chứa thứ tự duyệt ồ thị bề rộng từ nút bắt ầu sẽ cho một cây nhị phân ặc biệt
Cây khung - Spanning Tree, có tính chất : là một tập con của Grahp G mà có tất cả các nút
ược bao bởi số cạnh tối thiểu, một cây khung sẽ không hình thành một vòng tuần hoàn và nó cũng
không thể bị ngắt giữa chừng
Khi ta bỏ các cạnh có nét ứt, ồ thị kết quả cây khung (spanning tree): A
Đồ thị kết quả có tính chất :
Mỗi nút của ồ thkết quả B C có thi ến từ nút khởi ầu khi
và chỉ khi nó ược ánh dấu “ ã duyệt
Duyệt ồ thị theo BFS scho ta hình ảnh
duyệt cây theo từng mc, từ mức 1 ến mức D F cui cùng, mức sau cách mức trước 1 cnh.
F
E
i
1
2
3
4
5
6
bfs[i]
A
B
C
D
F
E
E
C
A
B
D
F
E
C
A
B
D
F
lOMoARcPSD| 45148588
10
E
4.4.1.3.Sơ ồ thuật toán BFS :
Thi
ế
t l
p Q[], bfs[];i=0;
!
EmptyQ(Q[])
i++;bfs[i]=u
bfs[i]=u
u=FrontQ(Q) ;
Xóa u khỏi Q;
begin
push(Q[u])
return
T
o
lienke(u)
v
lienke(u)
{
nếu
v
chưa duyệt thì
p
ush(Q,v)}
lOMoARcPSD| 45148588
4.4.2.Thuật toán duyệtthị theo chiều sâu – DFS
Duyệt ồ thị theo chiều sâu DFS gần giống với kiểu duyệt cây nhị phân DLR, luôn i từ nút xut phát
tới nút xa nhất cho tới khi không i tiếp ược nữa thì quay lại nút trước ó.
1)Tổ chức dữ liệu : ây sử dụng một mảng dfs[] lưu các nút ã ược duyệt và một ngăn xếp Stack
phục vụ cho quá trình duyệt. Trên mỗi bước lặp chọn mt nút bt ktrong các nút liền kề với nút
ang xét, lặp lại vic y cho ến lúc không i tiếp ược nữa; sau ó trở lại nút trước ó và tìm ường sang
nút khác nếu còn. Khi một nút mà các nút kề nó không i tiếp ược na và ã ánh dấu duyệt rồi thì s
xóa nó khỏi ngăn xếp Stack. Thuật toán kết thúc khi Stack ã
rỗng. 2)Các bước tổng quát của thuật toán DFS:
1.Đưa nút khởi ầu s vào ngăn xếp Stack và vào dfs[];
2.while(!Empty(stack){ w = lấy ra nút ở ỉnh ngăn xếp;
if(w ã duyệt) break;//thoát ra trở về while Duyt
w;//thực hiện một thao tác nào ó với w;
Đánh dấu w ã duyệt : ưa w vào danh sách dfs[];
Tạo tập các nút liền kề của w : V(w);
for (vV(w); v chưa ược duyệt-chưa có trong dfs[])
ưa v vào Stack; }
Hình 9
3)Vận dụng vào bài toán cụ thể Cho ồ thị như hình 9.
Ta tiến hành các bước với ồ thị như sau :
Khi tạo mảng dfs : Và ngăn xếp stack
Có thể bắt ầu duyệt t
một nút bt kỳ, ở ây ta
chọn nút S(start).
Đặt i là chỉ số của
mảng dfs[], mỗi nút dfs[i] có 2 thành phần Tên_nút
Thứ tự duyệt; t k là ếm số lần lặp DFS.
Bắt ầu từ i= 0 ; gán dfs[0].Tên_nút= S ; dfs[0].Thứ_tự_duyệt
= 1.
k= 0; Đưa nút S vào stack
Lần lặp while thứ 1(k++=1):
Lấy ỉnh ngăn xếp là S, xét tập nút liền kề của S
={A,B}, chọn nút liền kề ầu tiên mà chưa duyt ưa
vào stack : ở ây lấy A, dfs[1].Tên_nút= A,
dfs[1].Thứ_tự_duyệt= 2, ưa nút A vào stack. Kiểm tra,
ngăn xếp chưa rỗng, lặp tiếp
Lần lặp while thứ 2 (k++=2)
Lấy A- ỉnh Stack, lập tập nút liền kề của A={S,C},
ưa nút liền kề ầu tiên chưa duyệt là C vào Stack, i++,
dfs[2].Tên_nút= C, dfs[2].Thứ_tự_duyệt= 3,
i
dfs[i].Tên nút
dfs[i].Thứ tự duyệt
A
S
C
A
S
S
S
A
B
C
D
E
S
A
B
C
D
E
0
1
2
3
4
5
S
A
B
C
D
E
1
2
lOMoARcPSD| 45148588
12
Kiểm tra, ngăn xếp chưa rỗng, lặp tiếp
Lần lặp while thứ 3 (k++=3):
Lấy ra ỉnh ngăn xếp là C, xét tập nút liền kề của C
={A,B,E,D}. Chọn nút liền kề ầu tiên chưa duyệt là
B ưa vào Stack. i++, dfs[3].Tên_nút= B,
dfs[3].Thứ_tự_duyệt= 4. Kiểm tra, ngăn xếp chưa
rỗng, lặp tiếp
Lần lặp while thứ 4(k++=4)
Lấy B- ỉnh ngăn xếp. Xét tập nút liền kề của B
={S,C,E}, Chọn nút liền kề ầu tiên chưa duyệt là E
ưa vào Stack. . i++, dfs[4].Tên_nút= E,
dfs[4].Thứ_tự_duyệt= 5. Kiểm tra, ngăn xếp chưa
rỗng, lặp
tiếp Lần
lặp while thứ 5(k++=5)
Lấy E ỉnh ngăn xếp. Xét tập nút liền kề của E
={B,C,D}, Chọn nút liền kề ầu tiên chưa duyệt là D
ưa vào Stack. i++, dfs[5].Tên_nút= D,
dfs[5].Thứ_tự_duyệt= 6. Kiểm tra, ngăn
xếp chưa rỗng, lặp tiếp
Lần lặp while thứ 6(k++=6)
Lấy ra ỉnh
ngăn xếp
D. Xét tập nút
liền kề của
D ={C,E}, Tất
cả các nút liền kề với D ều ã duyệt nên xóa D ra khi
stack
Kiểm tra, ngăn xếp chưa rỗng, lặp tiếp
Lần lặp while thứ 7(k++=7) :
B
C
A
S
E
B
C
A
S
D
E
B
C
A
S
E
B
C
A
S
S
A
B
C
D
E
3
4
S
A
B
C
D
E
5
lOMoARcPSD| 45148588
Lấy ra ỉnh ngăn xếp là E
Tất cả các nút liền kề với E ều ã duyệt nên xóa E ra
khỏi Stack
Kiểm tra, ngăn xếp chưa rỗng, lặp tiếp
Lần lặp while thứ 8 (k++=8):
Lấy ra ỉnh ngăn xếp là B
Tất cả các nút liền kề với B ều ã duyệt nên xóa
B ra khỏi Stack. Kiểm tra, ngăn xếp chưa rỗng, lặp
tiếp
Lần lặp while thứ 9(k++=9)
Lấy ra ỉnh ngăn xếp là C
Tất cả các nút liền kề với C ều ã duyệt nên xóa C ra
khỏi Stack. Kiểm tra, ngăn xếp chưa rỗng, lặp tiếp
Lần lặp while thứ 10 (k++=10):
Lấy ra ỉnh ngăn xếpA. Tất cả các nút liền k
với A ều ã duyệt nên xóa A ra khỏi Stack
Kiểm tra, ngăn xếp chưa rỗng, lặp tiếp
Lần lặp while thứ 11(k++=11) :
Lấy ra ỉnh ngăn xếp là S.Tất cả các nút liền kề với
S ều ã duyệt nên xóa S ra khỏi stack
Kiểm tra, ngăn xếp rỗng, kết thúc lặp Mảng
dfs[] có kết quả là :
i =
0
1
2
3
4
5
dfs[i].dulieu =
S
A
C
B
E
D
dfs[i].ThuTuDuyet =
1
2
3
4
5
6
Cũng giống như duyệt theo bề ngang, khi duyt theo chiều sâu ta có thể bắt ầu từ một nút bt kỳ. Kết
quả duyệt scho mt cây khung (spanning tree) S-A-C-B-E-D :
B
C
A
S
C
A
S
A
S
S
S
A
B
C
D
E
lOMoARcPSD| 45148588
14
nhiều bước hay cả một khối các phép toán mà khi lập trình tạo thành một hàm.
Bài tập. 1) Đọc,tìm hiểu thuật toán duyệt ththeo bề ngang và theo chiều sâu.
2) Cho ồ th:
Hãy trình bày từng bước duyệt ồ ththeo bề ngang và
theo chiều sâu, có thể bắt ầu từ một nút bất k.
Sơ ồ
thu
t toán DFS :
Thu
t toán có th
b
ắt ầ
u b
ng m
t nút b
t k
.
Chú ý
trong các sơ ồ
:
kh
i thu
t toán , kh
i ký hi
u m
t quá trình g
m
Begin
Push(s),k=0
Empty(stack)
!
L
y ra nœt
đỉnh ngăn xế
p s =Top()
Ch
n nút li
n k
u tiên v
i nút s
chưa duyệ
t và
chưa có trong
dfs[]
, ưa vào
Stack.
N
ế
u t
t c
các nút li
n k
v
i s
u
ã duyệ
t thì xóa nút s
nh
stack
Exit
1
4
2
3
5
8
6
7
| 1/16

Preview text:

lOMoAR cPSD| 45148588
Bài 6 Chương 4. ĐỒ THỊ - GRAPH 4.1.Khái niệm
Cấu trúc dữ liệu ồ thị (graph) là kiểu cấu trúc tổng quát hơn cấu trúc cây TREE ã học. Sơ ồ mạng
lưới (network) là một dạng ứng dụng biến thể của ồ thị.
Một ồ thị ở dạng biểu diễn hình ảnh là các cặp nút và cạnh nối với nhau.
Một ồ thị ở dạng biểu diễn toán học là một tập hợp (V, E), trong ó V(vertices) là tập các nút và
E(edges) là tập các cạnh nối các cặp nút.
Một ồ thị mà các cạnh nối nút không có hướng gọi là ồ thị vô hướng, các cạnh có hướng gọi là ồ thị có hướng.
Ví dụ. trong ồ thị ở hình 1 dưới ây, tập cnút V= {A,B,C,D,E} , tập các cạnh E={AB, AC, BC, BD,CD,DE} B E A Hình 1. C D Các khái ni ệ m :
- Nút kề nhau : 2 nút ược nối với nhau bằng 1 cạnh. Ví dụ A kề B, A kề C,.., D kề E.
- Đường : biểu diễn một dãy các cạnh nối liền các nút. Trong hình trên, ACDE hoặc ABDE
biểu diễn 2 ường i từ A tới E.
4.2.Đồ thị có hướng 4.2.1.Khái niệm
Khi các nút nối với nhau bằng các cạnh có hướng thì ta có một ồ thị có hướng. Kiểu cấu trúc cây –
Tree – ã xét trước ây là một dạng riêng của ồ thì có hướng, trong cấu trúc cây, các nút luôn xét theo
hướng i từ nút gốc ến các nút lá. Trong ồ thị trên, A là nút gốc, E là nút lá và có 3 ường có thể i từ A -
> E là ABDE, ACDE, ABCDE. Khác với Cây, trong ồ thị có hướng, có thể ở một nút có hướng quay
vòng tại nút ó (giống hình ảnh một bùng binh, vòng xoay ở nút giao thông có nhiều ường giao nhau).
Đồ thị có hướng ược áp dụng nhiều trong thực tế, tạo thành một lĩnh vực riêng là sơ ồ mạng lưới, ược
vận dụng giải các bài toán như quản lý thi công các công trình xây dựng, quản lý mạng lưới iện, lập
kế hoạch sản xuất kinh doanh của các doanh nghiệp…
4.2.2.Cài ặt ồ thị có hướng
a)Ma trận kề - Adjacency matrix

Cách cài ặt thông dụng là dùng ma trận kề của một ồ thị có n nút, là một ma trận vuông A có n hàng n cột A[n][n], trong ó : 1 nếu
có cạnh nối nút i ến nút j A[i][j] =
0 nếu không có cạnh nối nút i ến nút j lOMoAR cPSD| 45148588
A – Adjacency matrix – ma trận kề
Như vậy, với ồ thị hình 1 ta lập ma trận kề : B A B C D E A 0 1 1 0 0 E A[] = B 0 0 1 1 0 A C 0 1 0 1 0 D 0 0 0 0 1 C D E 0 0 0 0 1
b)Mảng giá trị các nút
Dùng mảng D[] lưu dữ liệu các nút, D[i] là dữ liệu của nút i. c)Danh sách kề
Trong ma trận kề thường có nhiều phần tử = 0, nếu lưu trữ tất cả sẽ lãng phí ô nhớ. Có cách khác lưu
trữ thông tin của ma trận kề tiết kiệm bộ nhớ bằng cách dùng danh sách liên kết theo hàng, mỗi danh
sách nút theo hàng sẽ là một mảng liên kết, ược duy trì bởi một con trỏ trỏ ến nút ầu của mảng nút ó,
mỗi phần tử tiếp theo là một nút có 2 thành phần : dữ liệu và liên kết, ược nối tiếp ra từ nút ầu của mảng nút.
Ký hiệu V[x] là con trỏ cho nút x. V[A] -> -> A next -> B next -> C next -> D next E NULL - V[B] -> B next -> C next -> D NULL -> E NULL V[C] -> -> C next D NULL -> E NULL V[D] -> -> D next E NULL V[E] -> E NULL
Nếu thay chữ cái tên nút bằng số thứ tự nút 1,2,.. thì ta danh sách kề có dạng : V[1] 1 next -> 2 next -> 3 next -> 4 next -> -> 5 NULL - V[2] -> 2 next -> 3 next -> 4 NULL -> 5 NULL V[3] -> -> 3 next 4 NULL -> 5 NULL V[4] -> -> 4 next 5 NULL 2 lOMoAR cPSD| 45148588 V[5] -> 5
NULL Hình 2. Danh sách kề
Để thiết lập danh sách liền kề như hình 2 trên, có các bước :
1)Khai báo mảng các con trỏ V[], mỗi phần tử có 2 thành phần du_lieu và next -
n là số nút tối a của ồ thị
2)Khởi tạo nút ầu tiên của nút i có dữ liệu, next ược gán NULL
{ V[i].du_lieu = du_lieu0; V[i].next = NULL}
Với mỗi nút i sẽ thêm các nút kế tiếp theo sơ ồ
như hình 2 cho ến hết, sử dụng hàm thêm nút vào cuối A
danh sách liên kết ã học ở bài 2. 3)Tăng i và lặp lại bước 2 cho ến khi i=n.
4.2.3.Duyệt tìm theo chiều sâu trên ồ thị có hướng B
Ý tưởng của duyệt tìm theo chiều sâu D C
Hình 3 là từ gốc i xuống nút con, thăm nút con của nút E
con…cho ến khi hết các cấp dưới của nút con F G H
mới quay lên duyệt nút con khác cho ến khi tìm ược.
Với ồ thị ở hình 3, ầu tiên thăm nút A, sau ó thăm nút A con B, thăm nút con E,
quay lên thăm nút con C, thăm tiếp các nút con của C là
F,G; quay lên thăm nút D, thăm tiếp nút con của D là H. B
Thông thường dùng cấu trúc ngăn xếp – stack ể tổ chức D C
duyệt ồ thị theo chiều sâu. E F G H
5.2.4.Duyệt tìm theo chiều ngang trên ồ thị có hướng
Theo cách duyệt này ta i theo chiều ngang
của từng cấp. Hình 4
Ví dụ : với ồ thị ở hình 4, ầu tiên i từ gốc A, sau
ó thăm các nút mức 1 có 3 nút B, C, D sau ó
thăm các nút mức 2 có 4 nút E, F, G, H.
Cách này giống như duyệt hết các nút ở mức thứ i rồi mới duyệt ở mức thứ i+1 trong cây nhị phân.
Thông thường dùng cấu trúc hàng ợi – queue ể tổ chức duyệt ồ thị theo chiều ngang
4.3.Đồ thị vô hướng
Khi các cạnh trong ồ thị không chỉ hướng thì ta có ồ thị vô hướng. Trong ồ thị vô hướng không có
một nút nào ược nối với chính nó (không có xoay vòng). lOMoAR cPSD| 45148588
Cài ặt ồ thị vô hướng cũng gần giống như ối với ồ thị có hướng, dùng ma trận kề, danh sách các cạnh. A
4.3.1.Cài ặt ồ thị vô hướng bằng ma trận kề
Cho ồ thị như hình 5.
Ma trận kề bây giờ có dạng : B C Hình 5 A B C D E F 2 C 1 0 0 1 0 2 D 0 1 1 0 1 D 2 E 0 0 0 1 0 2 A 0 1 1 0 0 0 E F A[] = B 1 0 0 1 0 0 F 0 0 1 1 1 0
Nhận xét : ma trận kề của ồ thị vô hướng luôn ối xứng qua ường chéo chính.
4.3.2.Cài ặt ồ thị vô hướng bằng danh sách các cạnh
Trong ồ thị vô hướng, mỗi nút ở ầu cạnh có thể nối với nhiều nút khác. Chẳng hạn khi biểu
diễn mạng lưới các ường phố của một khu phố, có những oạn ường mà một ầu là ngã 6, một ầu là ngã tư : A B Nút 1 Nút 2 Hình 6 .
Danh sách các cạnh là khi ta ánh số thứ tự các nút, mỗi cạnh sẽ ược lưu trữ thông tin dạng CẠNH –
NÚT (Edge Node), sẽ lưu nút V[i] mà cạnh ược nối tới. Trong hình 6 trên, cạnh nối A-B là cạnh nối
nút V[1] và V[2]; ồng thời phải hiểu V[1] là iểm cuối của cạnh khác và V[2] cũng vậy. Ta dùng
mảng các con trỏ V[i], mỗi con trỏ V[i] trỏ ến nút ầu lưu trữ dữ liệu của các nút, nút ầu V[i] này có
trường liên kết trỏ ến một cạnh- nút mà có nút i là một trong các iểm cuối của nó.
Mỗi phần tử ei trong danh sách các cạnh có dạng : V[1] e1 V[2] 4 lOMoAR cPSD| 45148588 LienKet[1] LienKet[2]
Khi cạnh với tính cách là 1 phần tử trong danh sách liên kết thì nó giống như 1 nút trong danh sách
liên kết ôi, có 2 con trỏ trái và phải liên kết ến nút bên trái và nút bên phải.
Với ồ thị ở hình 5 trên, ta có danh sách cạnh như sau: 1.AB A 2.AC e1 e2 3.BD B C 4.CD e4 5.CE e3 e5 6.DE D Hình 7 7.DF e6 e7 8.EF E e8 F
Chú ý : một cạnh ã ược kê khai thì không kê khai lại, ví dụ ã khai báo cạnh AB thì không khai BA nữa.
4.4.Các thuật toán cơ bản trên ồ thị
Trong lý thuyết ồ thị, có 2 kiểu duyệt cơ bản là duyệt theo chiều ngang và duyệt theo chiều sâu.
Duyệt ồ thị theo chiều ngang (breadth-first searching BFS ) gần giống với duyệt cây nhị phân theo
các mức trong cây, duyệt ồ thị theo chiều sâu(depth-first searching DFS) gần giống với duyệt cây
theo tiền tự DLR; Chỉ có chỗ khác khác biệt là trong ồ thị không có nút gốc cố ịnh, việc duyệt có thể
bắt ầu từ một nút bất kỳ.
4.4.1.Thuật toán duyệt ồ thị theo bề ngang – BFS
Ý tưởng của thuật toán duyệt ồ thị theo bề ngang là duyệt các nút của ồ thị theo các mức theo
khoảng cách xa dần nút gốc(nút ược khởi ầu duyệt), mức sau cách mức trước 1 cạnh tính từ nút bắt ầu.
Giải thích thuật ngữ : trong các thuật toán BFS và DFS có sử dụng cụm từ “ ã duyệt”, một số tài
liệu dùng với ý nghĩa không rõ ràng, dễ nhầm lẫn. Trong bài này nói “ ã duyệt” là ã có một tác ộng
vào nút ó, chẳng hạn ã ánh dấu “ ã duyệt”, ang thực hiện lập tập nút liền kề của nó, xét xem các nút
trong tập liền kề của nó ã ược duyệt chưa ể ưa vào hàng ợi…
4.4.1.1.Giới thiệu thuật toán
Xét bài toán : cho một ồ thị vô hướng không có trọng số G=(V,E), trong ó V là tập các nút, E là tập
các cạnh; có một nút khởi ầu s ∈ V .
Để duyệt ồ thị theo BFS, sẽ sử dụng cấu trúc hàng ợi Q[] và danh sách chưa ược sắp là dãy bfs[] cài
ặt kiểu mảng array. Hàng ợi Q[] dùng cho việc thăm qua các nút, lưu các nút ã ược thăm và chờ ợi
duyệt, nút nào ược ánh dấu “ ã duyệt” sẽ xóa khỏi hàng ợi rồi sau ó mới thực hiện các thao tác liên lOMoAR cPSD| 45148588
quan ến nó. Mảng bfs[] ể lưu kết quả, chỉ số i của mảng bfs[i] là số thứ tự duyệt của nút , còn giá trị
của bfs[i] sẽ là số hiệu nút, ví dụ bfs[6] = E có nghĩa là nút E có thứ tự duyệt là 6 . Khởi ầu, chưa
duyệt nút nào, gán tất cả các bfs[i] = 0, sau ó lần lượt các lần duyệt i=1,bfs[1] = nhất>, i=2,bfs[2]= ,.... A
Cho m ột ồ th ị như sau : B C D Hình 8 E F
Thuật toán duyệt ồ thị theo bề ngang là duyệt các nút của ồ thị theo các mức theo khoảng cách xa
dần nút gốc(nút ược khởi ầu duyệt), mức thứ i+1 cách xa nút gốc hơn mức thứ i một cạnh. Với ồ thị
ở hình 8, mức 1 chỉ có nút khởi ầu A; mức 2 có nút B và C, mức 3 có nút D và F, mức 4 có một nút E.
4.4.1.2.Các bước của thuật toán BFS
Đầu vào : ồ thị G=(V,E), V là tập các nút, E là tập các cạnh.
Đầu ra : ồ thị Gkết quả =(V,E), với iều kiện mỗi nút của ồ thị kết quả ược nối với nút khởi ầu s khi và
chỉ khi nó ã ược ánh dấu “ ã duyệt”.
1.Thiết lập hàng ợi Q[] và mảng bfs[], khởi tạo nút s ưa vào Q;
Mảng bfs[] ược dùng ể lưu thứ tự duyệt các nút, bfs[i] = , nghĩa là chỉ số i của dãy
bfs[i] là thứ tự duyệt các nút, ược ngầm gán từ 1 ÷ n, n là số nút, còn giá trị của bfs[i] là tên nút
A/B/C/D/E/F, lúc ầu sẽ khởi tạo bfs[1]=…=bfs[n]=0. Ví dụ, bfs[1]=A ược hiểu là nút A duyệt ầu
tiên, hay bfs[1]= C nghĩa là nút duyệt ầu tiên là nút C. Trong ồ thị vô hướng, thuật toán duyệt theo
bề ngang BFS có thể bắt ầu duyệt từ một nút bất kỳ, nút ó ược lưu ở bfs[1] . Thuật toán BFS ược khái quát như sau:
1.Nút s ược chọn khởi ầu duyệt, ký hiệu là u là nút ầu của mỗi bước lặp duyệt, nên bắt ầu duyệt sẽ
coi u=s và u ược ưa vào hàng ợi Q[]. Trong bfs[] chưa có nút nào ược duyệt; ký hiệu i là thứ tự duyệt
của 1 nút, k là thứ tự bước lặp thuật toán, lúc ầu gán i=0; k=0;
2.while (Q không rỗng) {Lần lặp thứ k=k+1;
Kiểm tra nút u ã duyệt chưa, nếu bfs[i] = u là ã duyệt thì trở lại ầu vòng lặp while :
Nếu u chưa duyệt thì duyệt u, ánh dấu u ang duyệt, thứ tự duyệt i=i+1 => bfs[i]=u; 6 lOMoAR cPSD| 45148588
Tạo danh sách các nút liền kề V{u}, kiểm tra ối với mọi v ⸦ V{u} chưa duyệt thì ưa v vào Q[];
Nếu ã xét hết các nút liền kề trong V{u}, thì xóa u khỏi ầu hàng ợi Q[]. Kiểm tra nếu Q chưa
rỗng thì lặp lại while. }
Với ồ thị ở hình 8, từng bước thực hiện như sau. 1
Tạo dãy bfs[6] và hàng ợi Q[6]
Bước 0. Đưa nút khởi ầu A vào Q i 0 bfs[i] Q A
Tiếp theo thực hiện các bước lặp trong vòng lặp while cho ến khi hàng ợi Q[] rỗng :
while (!Empty(Q[]){Trong vòng lặp này thực hiện các lệnh :
▪ Lấy ra nút ầu Q ặt là u (chưa xóa u ở hàng ợi) ; nút khởi ầu là u=A. ▪ Xóa u khỏi hàng ợi Q[];
▪ Kiểm tra xem u ã duyệt chưa, nếu ã duyệt ( ã có bfs[i]=u) thì bỏ qua các phần sau, lặp lại while
▪ Duyệt u, ánh dấu u ang duyệt, thứ tự duyệt i=i+1 => bfs[i]=u;
▪ Tạo tập các nút liền kề với u dưới dạng danh sách lienke(u);
▪ Lần lượt xét từng nút v thuộc lienke(u), nếu v chưa ược duyệt (chưa có bfs[j]=v;j=1÷i) sẽ ưa v vào Q[]; }
Lần lặp 1. k=k+1=1; Lấy nút A ầu hàng ợi, duyệt nút A,
Xóa nút A khỏi hàng ợi Q[];
Kiểm tra xem u ã duyệt chưa : nếu ã duyệt thì bỏ qua các phần sau, lặp lại while ở
ây các bfs[]=0 , chưa duyệt.
i=i+1, ghi nút A vào thứ tự duyệt i=1 vào bfs[1]=A, i 1 A bfs[i] A 0 0 0 0
Tạo tập các nút liền kề với nút A: lienke(A)={B,C} (nút A nối với nút B , C). Lần
lượt xét 2 nút B,C này, chúng chưa ược duyệt vì chưa có mặt trong bfs[] nên ưa 2
nút này vào hàng ợi Q[], vẽ thêm nút B, C vào cây : B C Q B C
Kiểm tra, Q chưa rỗng nên lặp tiếp vòng while.
Lần lặp 2. k=k+1=2. Lấy nút B ở ầu hàng ợi Q[]. Duyệt nút B, xóa nút B khỏi hàng ợi Q[]; Q C i 1 2 bfs[i] A B lOMoAR cPSD| 45148588
Kiểm tra xem nút B ã duyệt chưa, nếu ã duyệt thì bỏ qua các phần sau, lặp lại while. A
Nhưng nút B chưa duyệt => Ghi nút B vào thứ tự duyệt i=i+1=2 => bfs[i] => bfs[2]= B; B
Tạo tập các nút liền kề lienke(B)={D}, nút B nối với nút D, thêm nút D vào C
cây : Xét nút D trong danh sách kề lienke(B), nó chưa ược duyệt nên ưa D vào hàng ợi Q[]: Q
Kiểm tra, Q chưa rỗng nên lặp tiếp vòng while. D C D
Lần lặp 3. k=k+1=3; Lấy nút C từ Q[]. Duyệt nút C, xóa nút C khỏi Q[]. Q D Kiểm tra xe
m u=C ã duyệt chưa, nếu ã duyệt thì bỏ qua các
phần sau, , lặp lại while, nhưng nút C chưa duyệt => i i
1 2 3 =i+1=3, ghi bfs[i]= bfs[3]= nút C; Tạo tập các
nút liền kề lienke(C)={D,F}, nút C nối với nút bfs[i] A
A B C 0 0 D,F; nút D ã có trong cây
nên chỉ thêm nút F vào cây: B C D Xét F nút D,F tr
ong danh sách kề lienke(C), chúng chưa ược duyệt nên ưa D,F vào Q[]: Q
Trên ồ thị dùng nét ứt nối C-D còn nút F bây giờ mới xét ến nên cạnh C-F vẽ D
nét liền. Đã xét tất cả các nút v⸦ lienke(C).
Kiểm tra, Q chưa rỗng nên lặp tiếp vòng while F
Lần lặp 4. k=k+1=4;Lấy nút D từ Q[]. Duyệt nút D, xóa nút D khỏi Q[]. Q
F Kiểm tra xem u=D ã duyệt chưa, nếu ã duyệt thì bỏ qua phần A sau, lặp lại while,
Nhưng nút D chưa duyệt => i=i+1=4, ghi nút D vào i
1 2 3 4 thứ tự duyệt i= 4 => bfs[4]=D; bfs[i] A B C D B C
Tạo tập các nút liền kề lienke(D)={B,C,E,F}, chỉ
xét nút E,F trong danh sách này vì chúng chưa ược duyệt và ưa chúng vào Q[].
Cạnh D-E vẽ nét liền, còn D-F dùng nét ứt nối vì cạnh C-F ã nối liền từ nút C. Q D Đã F E F xét
tất cả các nút v⸦ lienke(D).
Kiểm tra, Q chưa rỗng nên lặp tiếp vòng while
Lần lặp 5. k=k+1=5; Lấy nút F từ Q[]. Duyệt nút F. xóa nút F khỏi Q[] : Q E F E F Kiểm tra xe
m nút F ã duyệt chưa, nếu ã duyệt thì bỏ qua các phần
sau, lặp lại while. Nhưng nút F chưa duyệt => i=i+1=5; i 1 2 3 4 5 Ghi nút F
vào thứ tự duyệt i=5 =>bfs[5]= F; fs[i] A B C D F E F E 8 lOMoAR cPSD| 45148588
Tạo tập các nút liền kề lienke(F)={E,D,C}.
Xét các nút trong danh sách kề lienke(F),chỉ có nút E chưa duyệt nên ưa E A vào Q[]. Q
Nút F có cạnh nối F-E, F-D, F-C, nút C, D ã duyệt, nút E ã ược nối từ nút D, B C
nên cạnh F-E ta dùng nét ứt.
Đã xét tất cả các nút v⸦ lienke(F). D
Kiểm tra, Q chưa rỗng nên lặp tiếp vòng while
Lần lặp 6 : k=k+1=6;Lấy nút E từ Q[]. Duyệt nútE5, xóa nút E khỏi Q[]. Q F E Kiểm tra xe
m E ã duyệt chưa, nếu ã duyệt thì bỏ qua các phần sau, lặp lại while, E F i
1 2 3 4 5 6 Vì nút E chưa duyệt => i=i+1=6; Ghi nút E
bfs[i] A B C D F E vào thứ tự duyệt i=6 -> bfs[6]= E; A
Tạo tập các nút liền kề lienke(E)={D,F}.
Xét tất cả các nút v⸦ lienke(E) ã duyệt hết nên không thêm vào Q[] Kiểm
tra, Q chưa Rỗng nên lặp tiếp vòng while. B C
Lần lặp 7 : k=k+1=7;Lấy nút F từ Q[]. xóa nút F khỏi Q[]. Q E
Kiểm tra nút F ã duyệt nên bỏ qua các phần sau, lặp lại while. Kiểm tra, Q D
chưa Rỗng nên lặp vòng while
Lần lặp 8 : k=k+1=8;Lấy nút E từ Q[]. xóa nút E khỏi Q[]. Q F
Kiểm tra nút E ã duyệt nên bỏ qua các phần sau, lặp lại while. E
Kiểm tra, Q Rỗng nên kết thúc vòng while Kết quả, dãy bfs có :
Thứ tự duyệt 1 2 3 4 5 6 Số hiệu nút A B C D F E
Ta có thể bắt ầu duyệt lại ồ thị theo bề rộng với một nút khác bất kỳ B/C/D/E/F.
Danh sách kết quả bfs[] chứa thứ tự duyệt ồ thị bề rộng từ nút bắt ầu sẽ cho một cây nhị phân ặc biệt
Cây khung - Spanning Tree, có tính chất : là một tập con của Grahp G mà có tất cả các nút
ược bao bởi số cạnh tối thiểu, một cây khung sẽ không hình thành một vòng tuần hoàn và nó cũng
không thể bị ngắt giữa chừng
Khi ta bỏ các cạnh có nét ứt, ồ thị kết quả là cây khung (spanning tree): A
Đồ thị kết quả có tính chất :
Mỗi nút của ồ thị kết quả B
C có thể i ến từ nút khởi ầu khi
và chỉ khi nó ược ánh dấu “ ã duyệt”
Duyệt ồ thị theo BFS sẽ cho ta hình ảnh
duyệt cây theo từng mức, từ mức 1 ến mức D
F cuối cùng, mức sau cách mức trước 1 cạnh. lOMoAR cPSD| 45148588 E
4.4.1.3.Sơ ồ thuật toán BFS : begin
Thi ế t l ậ p Q[], bfs[];i=0; push(Q[u]) ! EmptyQ(Q[]) return u=FrontQ(Q) ; Xóa u khỏi Q; bfs[i]=u i++;bfs[i]=u T ạ o lienke(u) Xét ∀ v ∊ lienke(u) { nếu v
chưa duyệt thì p ush(Q,v)} 10 lOMoAR cPSD| 45148588
4.4.2.Thuật toán duyệt ồ thị theo chiều sâu – DFS
Duyệt ồ thị theo chiều sâu DFS gần giống với kiểu duyệt cây nhị phân DLR, luôn i từ nút xuất phát
tới nút xa nhất cho tới khi không i tiếp ược nữa thì quay lại nút trước ó.
1)Tổ chức dữ liệu : ở ây sử dụng một mảng dfs[] lưu các nút ã ược duyệt và một ngăn xếp Stack
phục vụ cho quá trình duyệt. Trên mỗi bước lặp chọn một nút bất kỳ trong các nút liền kề với nút
ang xét, lặp lại việc này cho ến lúc không i tiếp ược nữa; sau ó trở lại nút trước ó và tìm ường sang
nút khác nếu còn. Khi một nút mà các nút kề nó không i tiếp ược nữa và ã ánh dấu duyệt rồi thì sẽ
xóa nó khỏi ngăn xếp Stack. Thuật toán kết thúc khi Stack ã
rỗng. 2)Các bước tổng quát của thuật toán DFS:
1.Đưa nút khởi ầu s vào ngăn xếp Stack và vào dfs[]; S
2.while(!Empty(stack){ w = lấy ra nút ở ỉnh ngăn xếp;
if(w ã duyệt) break;//thoát ra trở về while Duyệt
w;//thực hiện một thao tác nào ó với w; A B
Đánh dấu w ã duyệt : ưa w vào danh sách dfs[];
Tạo tập các nút liền kề của w : V(w);
for (v⸦ V(w); v chưa ược duyệt-chưa có trong dfs[]) ưa v vào Stack; } C Hình 9
3)Vận dụng vào bài toán cụ thể Cho ồ thị như hình 9. D E
Ta tiến hành các bước với ồ thị như sau : Khởi tạo mảng dfs : Và ngăn xếp stack i
Có thể bắt ầu duyệt từ 0 S dfs[i].Tên nút
một nút bất kỳ, ở ây ta dfs[i].Thứ tự duyệt chọn nút S(start). Đặt i là chỉ số của 1 2
mảng dfs[], mỗi nút dfs[i] có 2 thành phần là Tên_nútA B
Thứ tự duyệt; ặt k là ếm số lần lặp DFS.
Bắt ầu từ i= 0 ; gán dfs[0].Tên_nút= S ; dfs[0].Thứ_tự_duyệt 3 = 1. C k= 0; Đưa nút S vào stack S
Lần lặp while thứ 1(k++=1): 4 5 A D E S
Lấy ỉnh ngăn xếp là S, xét tập nút liền kề của S S
={A,B}, chọn nút liền kề ầu tiên mà chưa duyệt ưa
vào stack : ở ây lấy A, dfs[1].Tên_nút= A,
dfs[1].Thứ_tự_duyệt= 2, ưa nút A vào stack. Kiểm tra, A B
ngăn xếp chưa rỗng, lặp tiếp 1
Lần lặp while thứ 2 (k++=2) C
Lấy A- ỉnh Stack, lập tập nút liền kề của A={S,C}, A C 2
ưa nút liền kề ầu tiên chưa duyệt là C vào Stack, i++, S
dfs[2].Tên_nút= C, dfs[2].Thứ_tự_duyệt= 3, D E lOMoAR cPSD| 45148588
Kiểm tra, ngăn xếp chưa rỗng, lặp tiếp
Lần lặp while thứ 3 (k++=3): S
Lấy ra ỉnh ngăn xếp là C, xét tập nút liền kề của C B
={A,B,E,D}. Chọn nút liền kề ầu tiên chưa duyệt là C
B ưa vào Stack. i++, dfs[3].Tên_nút= B, A
dfs[3].Thứ_tự_duyệt= 4. Kiểm tra, ngăn xếp chưa A B 3 S rỗng, lặp tiếp
Lần lặp while thứ 4(k++=4) C
Lấy B- ỉnh ngăn xếp. Xét tập nút liền kề của B E
={S,C,E}, Chọn nút liền kề ầu tiên chưa duyệt là E B
ưa vào Stack. . i++, dfs[4].Tên_nút= E, C D E 4
dfs[4].Thứ_tự_duyệt= 5. Kiểm tra, ngăn xếp chưa A rỗng, lặp S tiếp Lần S lặp while D thứ 5(k++=5) Lấy E ỉnh E
ngăn xếp. Xét tập nút liền kề của E ={B,C,D}, B
Chọn nút liền kề ầu tiên chưa duyệt là D A B ưa vào C
Stack. i++, dfs[5].Tên_nút= D, A
dfs[5].Thứ_tự_duyệt= 6. Kiểm tra, ngăn xếp chưa rỗng, lặp tiếp S C Lần lặp E
while thứ 6(k++=6) Lấy ra ỉnh B 5 ngăn xếp D E là C D. Xét tập nút A liền kề của S D ={C,E}, Tất
cả các nút liền kề với D ều ã duyệt nên xóa D ra khỏi stack
Kiểm tra, ngăn xếp chưa rỗng, lặp tiếp
Lần lặp while thứ 7(k++=7) : 12 lOMoAR cPSD| 45148588
Lấy ra ỉnh ngăn xếp là E S
Tất cả các nút liền kề với E ều ã duyệt nên xóa E ra B khỏi Stack C
Kiểm tra, ngăn xếp chưa rỗng, lặp tiếp A A B S
Lần lặp while thứ 8 (k++=8):
Lấy ra ỉnh ngăn xếp là B C
Tất cả các nút liền kề với B ều ã duyệt nên xóa C A
B ra khỏi Stack. Kiểm tra, ngăn xếp chưa rỗng, lặp S tiếp D E
Lần lặp while thứ 9(k++=9) A
Lấy ra ỉnh ngăn xếp là C S
Tất cả các nút liền kề với C ều ã duyệt nên xóa C ra
khỏi Stack. Kiểm tra, ngăn xếp chưa rỗng, lặp tiếp
Lần lặp while thứ 10 (k++=10):
Lấy ra ỉnh ngăn xếp là A. Tất cả các nút liền kề S
với A ều ã duyệt nên xóa A ra khỏi Stack
Kiểm tra, ngăn xếp chưa rỗng, lặp tiếp
Lần lặp while thứ 11(k++=11) :
Lấy ra ỉnh ngăn xếp là S.Tất cả các nút liền kề với
S ều ã duyệt nên xóa S ra khỏi stack
Kiểm tra, ngăn xếp rỗng, kết thúc lặp Mảng
dfs[] có kết quả là : i = 0 1 2 3 4 5 dfs[i].dulieu = S A C B E D
dfs[i].ThuTuDuyet = 1 2 3 4 5 6
Cũng giống như duyệt theo bề ngang, khi duyệt theo chiều sâu ta có thể bắt ầu từ một nút bất kỳ. Kết
quả duyệt sẽ cho một cây khung (spanning tree) S-A-C-B-E-D : lOMoAR cPSD| 45148588
Sơ ồ thu t toán DFS : Begin Push(s),k=0 !Em pty(stack) Exit L ấ y ra nœt
ở đỉnh ngăn xế p s =Top()
Ch ọ n nút li ề n k ề ầ u tiên v ớ i nút s
mà chưa duyệ t và chưa có trong dfs[] , ưa vào Stack.
N ế u t ấ t c ả các nút li ề n k ề v ớ i s ề u
ã duyệ t thì xóa nút s ở ỉ nh stack
Thu ậ t toán có th ể b ắt ầ u b ằ ng m ộ t nút b ấ t k ỳ . Chú ý :trong các
sơ ồ kh ố i thu ậ t toán , kh ố i ký hi ệ u m ộ t quá trình g ồ m
nhiều bước hay cả một khối các phép toán mà khi lập trình tạo thành một hàm.
Bài tập. 1) Đọc,tìm hiểu thuật toán duyệt ồ thị theo bề ngang và theo chiều sâu. 3 6 2) Cho ồ thị : 1 7
Hãy trình bày từng bước duyệt ồ thị theo bề ngang và
theo chiều sâu, có thể bắt ầu từ một nút bất kỳ. 2 4 5 8 14