Bài giảng Toán rời rạc: Tìm kiếm trên đồ thị (Version 0.5) cung cấp cho người học những nội dung kiến thức như: Biểu diễn đồ thị, tìm kiếm theo chiều sâu trên đồ thị vô hướng, tìm kiếm theo chiều sâu trên đồ thị có hướng, thành phần liên thông mạnh. Mời các bạn cùng tham khảo.
Trang 1Tìm kiếm trên đồ thị (Version 0.5)
Trần Vĩnh Đức
HUST
Ngày 16 tháng 9 năm 2019
Trang 2Tài liệu tham khảo
▶ S Dasgupta, C H Papadimitriou, and U V Vazirani,
Algorithms, July 18, 2016.
▶ Chú ý: Nhiều hình vẽ trong tài liệu được lấy tùy tiện mà chưaxin phép
Trang 3Nội dung
Biểu diễn đồ thị
Tìm kiếm theo chiều sâu trên đồ thị vô hướng
Tìm kiếm theo chiều sâu trên đồ thị có hướng
Thành phần liên thông mạnh
Trang 4Đồ thị
▶ Một đồ thị xác định bởi một tập đỉnh (còn gọi là nút) V và bởi các cạnh E giữa các cặp đỉnh được chọn.
▶ Đồ thị có thể vô hướng: cạnh e = {u, v}
▶ hoặc có hướng e = (u, v).
Trang 7Biểu diễn đồ thị dùng danh sách kề
Trang 8Dùng danh sách kề có hiệu quả?
▶ Có thể liệt kê các đỉnh kề với một đỉnh cho trước một cách
hiệu quả
▶ Nó cần không gian lưu trữ là O( |V| + |E|) Ít hơn O(|V|2) rấtnhiều khi đồ thị ít cạnh
Trang 9Nội dung
Biểu diễn đồ thị
Tìm kiếm theo chiều sâu trên đồ thị vô hướng
Tìm kiếm theo chiều sâu trên đồ thị có hướng
Thành phần liên thông mạnh
Trang 101: 0
2: 0
3: 5 4
4: 5 6 3 5: 3 4 0
6: 0 4 7: 8 8: 7
9: 11 10 12 10: 9
11: 9 12 12: 11 9 tinyG.txt
Output for list-of-edges input
V
E
first adjacent vertex in input
is last on list
second representation
of each edge appears in red
Trang 11Tìm đường trong mê cung
P1: OSO/OVY P2: OSO/OVY QC: OSO/OVY T1: OSO
das23402 Ch03 GTBL020-Dasgupta-v10 August 10, 2006 19:18
Figure 3.2 Exploring a graph is rather like navigating a maze.
A
C B
F D
C
F K
Depth-first search is a surprisingly versatile linear-time procedure that reveals a
wealth of information about a graph The most basic question it addresses is,
What parts of the graph are reachable from a given vertex?
To understand this task, try putting yourself in the position of a computer that has just been given a new graph, say in the form of an adjacency list This representation offers just one basic operation: finding the neighbors of a vertex With only this primitive, the reachability problem is rather like exploring a labyrinth (Figure 3.2) You start walking from a fixed place and whenever you arrive at any junction (vertex) there are a variety of passages (edges) you can follow A careless choice of passages might lead you around in circles or might cause you to overlook some accessible part of the maze Clearly, you need to record some intermediate information during exploration.
This classic challenge has amused people for centuries Everybody knows that all you need to explore a labyrinth is a ball of string and a piece of chalk The chalk prevents looping, by marking the junctions you have already visited The string always takes you back to the starting place, enabling you to return to passages that you previously saw but did not yet investigate.
How can we simulate these two primitives, chalk and string, on a computer? The chalk marks are easy: for each vertex, maintain a Boolean variable indicating whether it has been visited already As for the ball of string, the correct cyber-
analog is a stack After all, the exact role of the string is to offer two primitive operations—unwind to get to a new junction (the stack equivalent is to push the new vertex) and rewind to return to the previous junction (pop the stack).
Instead of explicitly maintaining a stack, we will do so implicitly via recursion (which is implemented using a stack of activation records) The resulting algorithm
11 / 58
CuuDuongThanCong.com https://fb.com/tailieudientucntt
Trang 12for each edge (v, u) ∈ E:
if not visited(u): explore(G, u)
postvisit(v)
Trang 13Ví dụ: Kết quả chạy explore(G, A)
P1: OSO/OVY P2: OSO/OVY QC: OSO/OVY T1: OSO
C
F K
Depth-first search is a surprisingly versatile linear-time procedure that reveals a
wealth of information about a graph The most basic question it addresses is,
What parts of the graph are reachable from a given vertex?
To understand this task, try putting yourself in the position of a computer that hasjust been given a new graph, say in the form of an adjacency list This representationoffers just one basic operation: finding the neighbors of a vertex With only thisprimitive, the reachability problem is rather like exploring a labyrinth (Figure 3.2)
You start walking from a fixed place and whenever you arrive at any junction (vertex)there are a variety of passages (edges) you can follow A careless choice of passagesmight lead you around in circles or might cause you to overlook some accessiblepart of the maze Clearly, you need to record some intermediate information duringexploration
This classic challenge has amused people for centuries Everybody knows that allyou need to explore a labyrinth is a ball of string and a piece of chalk The chalkprevents looping, by marking the junctions you have already visited The stringalways takes you back to the starting place, enabling you to return to passages thatyou previously saw but did not yet investigate
How can we simulate these two primitives, chalk and string, on a computer? Thechalk marks are easy: for each vertex, maintain a Boolean variable indicatingwhether it has been visited already As for the ball of string, the correct cyber-
analog is a stack After all, the exact role of the string is to offer two primitive operations—unwind to get to a new junction (the stack equivalent is to push the new vertex) and rewind to return to the previous junction (pop the stack).
Instead of explicitly maintaining a stack, we will do so implicitly via recursion(which is implemented using a stack of activation records) The resulting algorithm
P1: OSO/OVY P2: OSO/OVY QC: OSO/OVY T1: OSO
Figure 3.4 The result of explore(A) on the graph of Figure 3.2.
I E
J
C F B
A D G H
For instance, while B was being visited, the edge B − E was noticed and, since E was as yet unknown, was traversed via a call to explore(E ) These solid edges form a tree (a connected graph with no cycles) and are therefore called tree edges.
The dotted edges were ignored because they led back to familiar terrain, to vertices
previously visited They are called back edges.
3.2.2 Depth-first search
The explore procedure visits only the portion of the graph reachable from its starting point To examine the rest of the graph, we need to restart the procedure elsewhere, at some vertex that has not yet been visited The algorithm of Figure 3.5,
called depth-first search (DFS), does this repeatedly until the entire graph has been
traversed.
Figure 3.5 Depth-first search.
procedure dfs(G ) for all v ∈ V:
visited(v) = false for all v ∈ V:
if not visited(v): explore(v)
The first step in analyzing the running time of DFS is to observe that each vertex is explore’d just once, thanks to the visited array (the chalk marks) During the exploration of a vertex, there are the following steps:
1 Some fixed amount of work—marking the spot as visited, and the pre/postvisit.
13 / 58
CuuDuongThanCong.com https://fb.com/tailieudientucntt
Trang 14Tìm kiếm theo chiều sâu
Trang 15Ví dụ: Đồ thị và Rừng DFS
P1: OSO/OVY P2: OSO/OVY QC: OSO/OVY T1: OSO
2 A loop in which adjacent edges are scanned, to see if they lead somewhere
new.
This loop takes a different amount of time for each vertex, so let’s consider all
vertices together The total work done in step 1 is then O(|V|) In step 2, over
the course of the entire DFS, each edge {x, y} ∈ E is examined exactly twice, once
during explore(x) and once during explore(y) The overall time for step 2 is
therefore O(|E |) and so the depth-first search has a running time of O(|V| + |E |),
linear in the size of its input This is as efficient as we could possibly hope for, since
it takes this long even just to read the adjacency list.
Figure 3.6 (a) A 12-node graph (b) DFS search forest.
Figure 3.6 shows the outcome of depth-first search on a 12-node graph, once again
breaking ties alphabetically (ignore the pairs of numbers for the time being) The
outer loop of DFS calls explore three times, on A, C , and finally F As a result,
there are three trees, each rooted at one of these starting points Together they
constitute a forest.
3.2.3 Connectivity in undirected graphs
An undirected graph is connected if there is a path between any pair of vertices The
graph of Figure 3.6 is not connected because, for instance, there is no path from A
to K However, it does have three disjoint connected regions, corresponding to the
following sets of vertices:
{A, B, E , I, J } {C , D, G, H, K , L} {F }.
These regions are called connected components: each of them is a subgraph that is
internally connected but has no edges to the remaining vertices When explore
is started at a particular vertex, it identifies precisely the connected component
containing that vertex And each time the DFS outer loop calls explore, a new
connected component is picked out.
15 / 58
CuuDuongThanCong.com https://fb.com/tailieudientucntt
Trang 16Bài tập
Xây dựng rừng DFS cho đồ thị sau với các đỉnh lấy theo thứ tự từđiển Vẽ cả những cạnh nét đứt
Trang 17Rừng DFS và số thành phần liên thông
2 A loop in which adjacent edges are scanned, to see if they lead somewhere
new
This loop takes a different amount of time for each vertex, so let’s consider all
vertices together The total work done in step 1 is then O(|V|) In step 2, over
the course of the entire DFS, each edge {x, y} ∈ E is examined exactly twice, once
during explore(x) and once during explore(y) The overall time for step 2 is
therefore O(|E |) and so the depth-first search has a running time of O(|V| + |E |),
linear in the size of its input This is as efficient as we could possibly hope for, since
it takes this long even just to read the adjacency list
Figure 3.6 (a) A 12-node graph (b) DFS search forest
D H
Figure 3.6 shows the outcome of depth-first search on a 12-node graph, once again
breaking ties alphabetically (ignore the pairs of numbers for the time being) The
outer loop of DFS calls explore three times, on A, C , and finally F As a result,
there are three trees, each rooted at one of these starting points Together they
constitute a forest.
3.2.3 Connectivity in undirected graphs
An undirected graph is connected if there is a path between any pair of vertices The
graph of Figure 3.6 is not connected because, for instance, there is no path from A
to K However, it does have three disjoint connected regions, corresponding to the
following sets of vertices:
{A, B, E , I, J } {C , D, G, H, K , L} {F }.
These regions are called connected components: each of them is a subgraph that is
internally connected but has no edges to the remaining vertices When explore
is started at a particular vertex, it identifies precisely the connected component
containing that vertex And each time the DFS outer loop calls explore, a new
connected component is picked out
Trang 18Tính liên thông trong đồ thị vô hướng
procedure explore(G, v)
visited(v) = true
previsit(v)
for each edge (v, u) ∈ E:
if not visited(u): explore(G, u)
postvisit(v)
procedure previsit(v)
ccnum[v] = cc
Trang 19Bài tập
Hãy cài đặt chương trình tìm số thành phần liên thông của một đồthị vô hướng
Trang 20previsit và postvisit
▶ Lưu thời gian lần đầu đến đỉnh trong mảng pre
▶ Lưu thời gian lần cuối rời khỏi đỉnh trong mảng post
▶ Để tính hai thông tin này ta dùng một bộ đếm clock, khởi
tạo bằng 1, và được cập nhật như sau:
procedure previsit(v) pre[v] = clock
clock = clock + 1procedure postvisit(v) post[v] = clock
clock = clock + 1
Trang 21Bài tập
Vẽ rừng DFS với cả số pre và post cho mỗi đỉnh cho đồ thị sau
Trang 22Tính chất của previsit và postvisit
Mệnh đề
Với mọi đỉnh u và v, hai khoảng
[ pre(u), post(u) ] và [ pre(v), post(v) ]
▶ hoặc là rời nhau,
▶ hoặc là có một khoảng chứa một khoảng khác
Tại sao? vì [ pre(u), post(u) ] là khoảng thời gian đỉnh u nằm
trong ngăn xếp Cấu trúc vào-sau, ra-trước đảm bảo tính chất này
Trang 23Nội dung
Biểu diễn đồ thị
Tìm kiếm theo chiều sâu trên đồ thị vô hướng
Tìm kiếm theo chiều sâu trên đồ thị có hướng
Thành phần liên thông mạnh
Trang 24Figure 3.7 DFS on a directed graph.
In further analyzing the directed case, it helps to have terminology for important
relationships between nodes of a tree A is the root of the search tree; everything else is its descendant Similarly, E has descendants F , G , and H , and conversely,
is an ancestor of these three nodes The family analogy is carried further: C is the
parent of D, which is its child.
For undirected graphs we distinguished between tree edges and nontree edges In the directed case, there is a slightly more elaborate taxonomy:
Back
Forward
Cross Tree
A
B
DFS tree
Tree edges are actually part of the DFS forest.
Forward edges lead from a node to a nonchild descendant in
the DFS tree.
Back edges lead to an ancestor in the DFS tree.
Cross edges lead to neither descendant nor ancestor; they
therefore lead to a node that has already been completely explored (that is, already postvisited).
Figure 3.7 has two forward edges, two back edges, and two cross edges Can you spot them?
Ancestor and descendant relationships, as well as edge types, can be read off directly from pre and post numbers Because of the depth-first exploration strategy, vertex
u is an ancestor of vertex v exactly in those cases where u is discovered first and
24 / 58
CuuDuongThanCong.com https://fb.com/tailieudientucntt
Trang 25Lời giải
P1: OSO/OVY P2: OSO/OVY QC: OSO/OVY T1: OSO
Figure 3.7 DFS on a directed graph.
In further analyzing the directed case, it helps to have terminology for important
relationships between nodes of a tree A is the root of the search tree; everything
else is its descendant Similarly, E has descendants F , G , and H , and conversely,
is an ancestor of these three nodes The family analogy is carried further: C is the
parent of D, which is its child.
For undirected graphs we distinguished between tree edges and nontree edges In
the directed case, there is a slightly more elaborate taxonomy:
Tree edges are actually part of the DFS forest.
Forward edges lead from a node to a nonchild descendant in
the DFS tree.
Back edges lead to an ancestor in the DFS tree.
Cross edges lead to neither descendant nor ancestor; they
therefore lead to a node that has already been completely explored (that is, already postvisited).
Figure 3.7 has two forward edges, two back edges, and two cross edges Can you
spot them?
Ancestor and descendant relationships, as well as edge types, can be read off directly
from pre and post numbers Because of the depth-first exploration strategy, vertex
u is an ancestor of vertex v exactly in those cases where u is discovered first and
25 / 58
CuuDuongThanCong.com https://fb.com/tailieudientucntt
Trang 26Các kiểu cạnh
P1: OSO/OVY P2: OSO/OVY QC: OSO/OVY T1: OSO
das23402 Ch03 GTBL020-Dasgupta-v10 August 10, 2006 19:18
Figure 3.7 DFS on a directed graph.
In further analyzing the directed case, it helps to have terminology for important
relationships between nodes of a tree A is the root of the search tree; everything
else is its descendant Similarly, E has descendants F , G , and H , and conversely,
is an ancestor of these three nodes The family analogy is carried further: C is the
parent of D, which is its child.
For undirected graphs we distinguished between tree edges and nontree edges In
the directed case, there is a slightly more elaborate taxonomy:
Back
Forward
Cross Tree
A
B
DFS tree
Tree edges are actually part of the DFS forest.
Forward edges lead from a node to a nonchild descendant in
the DFS tree.
Back edges lead to an ancestor in the DFS tree.
Cross edges lead to neither descendant nor ancestor; they
therefore lead to a node that has already been completely explored (that is, already postvisited).
Figure 3.7 has two forward edges, two back edges, and two cross edges Can you
spot them?
Ancestor and descendant relationships, as well as edge types, can be read off directly
from pre and post numbers Because of the depth-first exploration strategy, vertex
u is an ancestor of vertex v exactly in those cases where u is discovered first and
rừng DFS
từ một nút tới một nút con cháu của nónhưng không thuộc rừng DFS
từ một nút tới một tổ tiên của nó
từ một nút tới một nút không phải tổtiên cũng không phải con cháu của nó
Trang 27Bài tập
Thực hiện thuật toán DFS trên mỗi đồ thị sau; nếu phải thực hiệnlựa chọn đỉnh, chọn đỉnh theo thứ tự từ điển Phân loại mỗi cạnh(tree edge, forward edge, back edge, hay cross edge) và đưa ra sốpre và post cho mỗi đỉnh
Trang 28Các khả năng cho cạnh (u, v)
Trang 29Mệnh đề
Một đồ thị có hướng có chu trình nếu và chỉ nếu thuật toán tìm
kiếm theo chiều sâu tạo ra back edge
Chứng minh
P1: OSO/OVY P2: OSO/OVY QC: OSO/OVY T1: OSO
Figure 3.7 DFS on a directed graph
In further analyzing the directed case, it helps to have terminology for important
relationships between nodes of a tree A is the root of the search tree; everything
else is its descendant Similarly, E has descendants F , G , and H , and conversely,
is an ancestor of these three nodes The family analogy is carried further: C is the
parent of D, which is its child.
For undirected graphs we distinguished between tree edges and nontree edges In
the directed case, there is a slightly more elaborate taxonomy:
Tree edges are actually part of the DFS forest.
Forward edges lead from a node to a nonchild descendant in
the DFS tree
Back edges lead to an ancestor in the DFS tree.
Cross edges lead to neither descendant nor ancestor; they
therefore lead to a node that has already been completelyexplored (that is, already postvisited)
Figure 3.7 has two forward edges, two back edges, and two cross edges Can you
spot them?
Ancestor and descendant relationships, as well as edge types, can be read off directly
from pre and post numbers Because of the depth-first exploration strategy, vertex
u is an ancestor of vertex v exactly in those cases where u is discovered first and
▶ Nếu (u, v) là back edge, thì
u ; v → u là một chu trình.
▶ Ngược lại, giả sử đồ thị có chutrình
C = v0→ v1 → · · · → v k → v0 Xét v i là đỉnh đầu tiên trong C
được thăm theo DFS Mọi đỉnhkhác trong chu trình sẽ đạt được từ
v i Vậy thì v i −1 → v i là back edge
29 / 58
CuuDuongThanCong.com https://fb.com/tailieudientucntt