Giáo trình Cấu trúc dữ liệu và giải thuật - Chương 13: Đồ thị

Một đồ thị (graph) G gồm một tập V chứa các đỉnh của đồ thị, và tập E chứa

các cặp đỉnh khác nhau từ V. Các cặp đỉnh này được gọi là các cạnh của G. Nếu e

= (ν, µ) là một cạnh có hai đỉnh ν và µ, thì chúng ta gọi ν và µ nằm trên e, và e

nối với ν và µ. Nếu các cặp đỉnh không có thứ tự, G được gọi là đồ thị vô hướng

(undirected graph), ngược lại, G được gọi là đồ thị có hướng (directed graph).

Thông thường đồ thị có hướng được gọi tắt là digraph, còn từ graph thường mang

nghĩa là đồ thị vô hướng. Cách tự nhiên để vẽ đồ thị là biểu diễn các đỉnh bằng

các điểm hoặc vòng tròn, và các cạnh bằng các đường thẳng hoặc các cung nối các

đỉnh. Đối với đồ thị có hướng thì các đường thẳng hay các cung cần có mũi tên chỉ

hướng. Hình 13.1 minh họa một số ví dụ về đồ thị.

Đồ thị thứ nhất trong hình 13.1 có các thành phố là các đỉnh, và các tuyến bay

là các cạnh. Trong đồ thị thứ hai, các nguyên tử hydro và carbon là các đỉnh, các

liên kết hóa học là các cạnh. Hình thứ ba là một đồ thị có hướng cho biết khả

năng truyền nhận dữ liệu trên mạng, các nút của mạng (A, B, , F) là các đỉnh và

các đường nối các nút là có hướng. Đôi khi cách chọn tập đỉnh và tập cạnh cho đồ

thị phụ thuộc vào giải thuật mà chúng ta dùng để giải bài toán, chẳng hạn bài

toán liên quan đến quy trình công việc, bài toán xếp thời khóa biểu,

 

pdf26 trang | Chuyên mục: Cấu Trúc Dữ Liệu & Giải Thuật | Chia sẻ: tuando | Lượt xem: 784 | Lượt tải: 0download
Tóm tắt nội dung Giáo trình Cấu trúc dữ liệu và giải thuật - Chương 13: Đồ thị, để xem tài liệu hoàn chỉnh bạn click vào nút "TẢI VỀ" ở trên
hay 
đổi mà chúng ta đã làm đối với tập X như sau: với mỗi w chưa có trong X, nếu có 
Hình 13.13 – Ví dụ về giải thuật Prim 
Chương 13 – Đồ thị 
Giáo trình Cấu trúc dữ liệu và Giải thuật 361
một cạnh nối ν và w, chúng ta xem thử tải trọng của cạnh này có nhỏ hơn 
distance[w] hay không, nếu quả thực như vậy thì distance[w] cần được cập 
nhật lại bằng trị của tải trọng này, và neighbor[w] sẽ là ν. 
Lấy ví dụ, chúng ta hãy xem xét mạng trong hình (a) của hình 13.13. Trạng 
thái ban đầu trong hình (b): Tập X (các đỉnh được tô màu) chỉ gồm đỉnh nguồn 0, 
và đối với mỗi đỉnh w, đỉnh được lưu trong neighbor[w] được chỉ bởi các mũi tên 
từ w. Trị của distance[w] là tải trọng của cạnh tương ứng. Khoảng cách từ 
nguồn đến đỉnh 1 là một trong những trị nhỏ nhất, nên 1 được thêm vào X như 
hình (c), và trong các phần tử của các mảng neighbor và distance chỉ có các 
phần tử tương ứng với đỉnh 2 và đỉnh 5 là được cập nhật lại. Đỉnh kế tiếp gần với 
các đỉnh trong X nhất là đỉnh 2, nó được thêm vào X như hình (d), trong này 
cũng đã chỉ ra các trị của các mảng đã được cập nhật lại. Các bước cuối cùng được 
minh họa trong hình (e), (f) và (g). 
13.6.3. Hiện thực 
Để hiện thực giải thuật Prim, chúng ta cần chọn một lớp để biểu diễn cho 
mạng. Sự tương tự của giải thuật này so với giải thuật tìm đường ngắn nhất trong 
phần trước giúp chúng ta quyết định thiết kế lớp Network dẫn xuất từ lớp 
Digraph. 
template 
class Network: public Digraph { 
public: 
 Network(); 
 void read(); // Định nghĩa lại để nhập thông tin về mạng. 
 void make_empty(int size = 0); 
 void add_edge(Vertex v, Vertex w, Weight x); 
 void minimal_spanning(Vertex source, 
 Network &tree) const; 
}; 
Chúng ta sẽ viết lại phương thức nhập read để đảm bảo rằng tải trọng của 
cạnh (ν, w) luôn trùng với tải trọng của cạnh (w, ν), với mọi ν và w, vì đây là một 
mạng vô hướng. Phương thức mới make_empty(int size) tạo một mạng có 
size đỉnh và không có cạnh. Phương thức khác, add_edge, thêm một cạnh có 
một tải trọng cho trước vào mạng. Chúng ta cũng đã giả sử rằng lớp Weight đã có 
đầy đủ các toán tử so sánh. Ngoài ra, người sử dụng cần khai báo trị lớn nhất có 
thể của Weight gọi là infinity. Phương thức minimal_spanning mà chúng ta 
sẽ viết ở đây sẽ tìm cây phủ tối tiểu và trả về qua tham biến tree. Tuy phương 
thức chỉ có thể tìm cây phủ khi thực hiện trên một mạng liên thông, nó cũng có 
thể tìm một cây phủ cho một thành phần liên thông có chứa đỉnh source trong 
mạng. 
Chương 13 – Đồ thị 
Giáo trình Cấu trúc dữ liệu và Giải thuật 362
template 
void Network::minimal_spanning(Vertex source, 
 Network &tree) const 
/* 
post: Xác định cây phủ tối tiểu trong thành phần liên thông có chứa đỉnh source của mạng. 
*/ 
{ 
 tree.make_empty(count); 
 bool component[graph_size]; // Các đỉnh trong tập X. 
 Vertex neighbor[graph_size];// Phần tử thứ i chứa đỉnh trước của nó sao cho khoảng 
// cách giữa chúng nhỏ nhất so với các khoảng cách từ 
// các đỉnh trước khác đã có trong tập X đến nó. 
 Weight distance[graph_size];// Các khoảng cách nhỏ nhất tương ứng với từng phần tử 
// trong mảng neighbor trên. 
 Vertex w; 
 for (w = 0; w < count; w++) { 
 component[w] = false; 
 distance[w] = adjacency[source][w]; 
 neighbor[w] = source; 
 } 
 component[source] = true; // Tập X chỉ có duy nhất đỉnh nguồn. 
 for (int i = 1; i < count; i++) { 
 Vertex v; // Mỗi lần lặp bổ sung thêm một đỉnh vào tập X. 
 Weight min = infinity; 
 for (w = 0; w < count; w++) if (!component[w]) 
 if (distance[w] < min) { 
 v = w; 
 min = distance[w]; 
 } 
 if (min < infinity) { 
 component[v] = true; 
 tree.add_edge(v, neighbor[v], distance[v]); 
 for (w = 0; w < count; w++) if (!component[w]) 
 if (adjacency[v][w] < distance[w]) { 
 distance[w] = adjacency[v][w]; 
 neighbor[w] = v; 
 } 
 } 
 else break; // Xong một thành phần liên thông trong đồ thị không liên thông. 
 } 
} 
Vòng lặp chính trong hàm trên thực hiện n-1 lần, với n là số đỉnh, và trong 
vòng lặp chính còn có hai vòng lặp khác, mỗi vòng lặp này thực hiện n-1 lần. Vậy 
các vòng lặp thực hiện (n-1)2 lần. Các lệnh bên ngoài vòng lặp chỉ hết O(n), nên 
thời gian chạy của hàm là O(n2). 
13.6.4. Kiểm tra giải thuật Prim 
Chúng ta cần chứng minh rằng, đối với đồ thị liên thông G, cây phủ S sinh ra 
bởi giải thuật Prim phải có tổng tải trọng trên các cạnh nhỏ hơn so với bất kỳ 
Chương 13 – Đồ thị 
Giáo trình Cấu trúc dữ liệu và Giải thuật 363
một cây phủ nào khác của G. Giải thuật Prim xác định một chuỗi các cạnh s1, s2, 
..., sn tạo ra cây phủ S. Như hình 13.14, s1 là cạnh thứ nhất được thêm vào tập Y 
trong giải thuật Prim, s2 là cạnh thứ hai được thêm vào, và cứ thế. 
Để chứng minh S là một cây phủ tối tiểu, chúng ta chứng minh rằng nếu m là 
một số nguyên, 0 ≤ m ≤ n, thì sẽ có một cây phủ tối tiểu chứa cạnh si với i ≤ m. 
Chúng ta sẽ chứng minh quy nạp trên m. Trường hợp cơ bản, khi m = 0, rõ ràng 
là đúng, vì bất kỳ cây phủ tối tiểu nào cũng đều phải chứa một tập rỗng các cạnh. 
Ngoài ra, khi chúng ta hoàn tất việc quy nạp, trường hợp cuối cùng với m = n chỉ 
ra rằng có một cây phủ tối tiểu chứa tất cả các cạnh của S, và chính là S. (Lưu ý 
rằng nếu thêm bất kỳ một cạnh nào vào một cây phủ thì cũng tạo ra một chu 
trình, nên bất kỳ cây phủ nào chứa mọi cạnh của S đều phải chính là S). Nói cách 
khác, khi chúng ta hoàn tất việc quy nạp, chúng ta đã chứng minh được rằng S 
chính là cây phủ tối tiểu. 
Như vậy chúng ta cần trình bày các bước quy nạp bằng cách chứng minh rằng 
nếu m < n và T là một cây phủ tối tiểu chứa các cạnh s i với i ≤ m, thì phải có một 
cây phủ tối tiểu U cũng chứa các cạnh trên và thêm một cạnh sm+1. Nếu sm+1 đã có 
trong T, chúng ta có thể đơn giản cho U = T, như vậy chúng ta có thể giả sử rằng 
sm+1 không là một cạnh trong T. Chúng ta hãy xem hình (b) của hình 13.14. 
Chúng ta hãy gọi X là tập các đỉnh trong S thuộc các cạnh s1, s2, ..., sm và R là 
tập các đỉnh còn lại trong S. Trong giải thuật Prim, cạnh sm+1 nối một đỉnh trong 
Hình 13.14 – Kiểm tra giải thuật Prim 
Chương 13 – Đồ thị 
Giáo trình Cấu trúc dữ liệu và Giải thuật 364
X với một đỉnh trong R, và sm+1 là một trong các cạnh có tải trọng nhỏ nhất nối 
giữa hai tập này. Chúng ta hãy xét ảnh hưởng của việc thêm cạnh sm+1 này vào T, 
như minh họa trong hình (c). Việc thêm vào này phải tạo ra một chu trình C, do 
mạng liên thông T rõ ràng là có chứa một đường đi có nhiều cạnh nối hai đầu của 
cạnh sm+1. Chu trình C phải có chứa một cạnh t ≠ sm+1 nối tập X với tập R, do nếu 
chúng ta di dọc theo đường đi khép kín C chúng ta phải đi vào tập X một số lần 
bằng với số lần chúng ta đi ra khỏi nó. Chúng ta hãy xem hình (d). Giải thuật 
Prim bảo đảm rằng tải trọng của sm+1 nhỏ hơn hoặc bằng tải trọng của t. Do đó, 
cây phủ mới U trong hình (e), có được từ T bằng cách loại đi t và thêm vào sm+1, 
sẽ có tổng tải trọng không lớn hơn tải trọng của T. Như vậy chúng ta đã có được 
U là một cây phủ tối tiểu của G, mà U chứa các cạnh s1, s2, ..., sm, sm+1. Điều này 
hoàn tất được quá trình quy nạp của chúng ta. 
13.7. Sử dụng đồ thị như là cấu trúc dữ liệu 
Trong chương này, chúng ta đã tìm hiểu chỉ một ít ứng dụng của đồ thị, nhưng 
chúng ta đã bắt đầu thâm nhập vào những vấn đề sâu sắc của các giải thuật về đồ 
thị. Nhiều giải thuật trong số đó, đồ thị xuất hiện như các cấu trúc toán học và đã 
nắm bắt được các đặc trưng thiết yếu của bài toán, thay vì chỉ là những công cụ 
tính toán cho ra được những lời giải của chúng. Lưu ý rằng trong chương này 
chúng ta đã nói về các đồ thị như là các cấu trúc toán học, chứ không như các cấu 
trúc dữ liệu, do chúng ta đã sử dụng chúng để đặc tả các vấn đề trong toán học, 
và để viết các giải thuật, chúng ta đã hiện thực các đồ thị trong các cấu trúc dữ 
liệu như danh sách hoặc bảng. Tuy vậy, rõ ràng là đồ thị tự bản thân nó có thể 
được xem như các cấu trúc dữ liệu - các cấu trúc dữ liệu mà có chứa các mối quan 
hệ giữa các dữ liệu phức tạp hơn những gì đã được mô tả trong một danh sách 
hoặc một cây. Do tính tổng quát và mềm dẻo, đồ thị là cấu trúc dữ liệu rất hiệu 
quả và đã tỏ rõ những giá trị của nó trong những ứng dụng cấp tiến như thiết kế 
hệ quản trị cơ sở dữ liệu chẳng hạn. Tất nhiên, một công cụ mạnh như vậy càng 
nên được sử dụng mọi khi cần thiết, nhưng việc sử dụng nó cần phải được kết hợp 
với việc xem xét một cách cẩn thận để sức mạnh của nó không làm cho cho chúng 
ta bị rối. Cách an toàn nhất trong việc sử dụng một công cụ mạnh là dựa trên sự 
chính quy; nghĩa là, chúng ta chỉ sử dụng công cụ mạnh trong những phương 
pháp đã được định nghĩa một cách cẩn thận và dễ hiểu. Do tính tổng quát của đồ 
thị, việc tuân thủ nguyên tắc vừa nêu ra trong việc sử dụng nó không phải luôn dễ 
dàng. 

File đính kèm:

  • pdfgiao_trinh_cau_truc_du_lieu_va_giai_thuat_chuong_13_do_thi.pdf