II. CÂY BAO TRÙM
3. Cây bao trùm bé nhất
3.1 Định nghĩa: Cho đồ thị n đỉnh G = <X, U> liên thông, n > 1. Mỗi cạnh u U ta gán cho nó trọng số l(u). Giả sử G' = <X, U') là cây bao trùm của G.
Ký hiệu gọi là trọng số hay là độ dài của G'.
Ký hiệu là tập các cây bao trùm của G, khi đó nếu cây bao trùm g thoả mãn l(g) = Min {l(g') / g' } thì ta nói rằng g là cây bao trùm bé nhất trong G.
3.2 Thuật toán tìm cây bao trùm bé nhất
Cho đồ thị G = <X, U> với số đỉnh n tìm cây bao trùm bé nhất của G.
3.2.1 Thuật toán Kruskal
Thuật toán sẽ xây dựng tập cạnh của cây bao trùm nhỏ nhất theo từng bước. Trước hết sắp xếp các cạnh của đồ thị G theo thứ tự không giảm của độ dài. Bắt đầu từ tập cạnh của cây là rỗng , ở mỗi bước ta sẽ lần lượt duyệt trong danh sách cạnh đã sắp xếp, từ cạnh có độ dài nhỏ đến cạnh có độ dài lớn hơn, để tìm ra cạnh mà việc bổ sung nó vào tập cạnh của cây không tạo thành chu trình trong tập này. Thuật toán sẽ kết thúc khi ta thu được 1 tập cạnh gồm n - 1 cạnh. Cụ thể thuật toán có thể mô tả như sau:
- Bước 1: Chọn cạnh u1 thoả mãn l(u1) = min {l(u) : u U} đặt U1 = {u1}
- Bước 2: Chọn u2 thoả mãn : l(u2) = min{l(u) : u U\U1} đặt U2 = {u1, u2}
...
- Bước k + 1: Giả sử đã có tập Uk = {u1, u2, ..., uk} chọn uk+1 thoả mãn l(uk+1) = min(l(u) : u U\Uk}
đặt Uk+1 = Uk {uk+1}
Chú ý ở các bước, cạnh mới được chọn phải không lập thành chu trình với các cạnh đã chọn ở các bước trước.
Thuật toán dừng lại lại ở bước thứ n - 1. Vậy cây T = <X, Un-1> tìm được là cây bao trùm ngắn nhất.
3.2.2 Thuật toán Prim
∑∈ ∈ = ' ) ( ) ' ( U u u l G l
Thuật toán Prim còn được gọi là phương pháp lân cận gần nhất. Trong phương pháp này, bắt đầu từ một đỉnh s tuỳ ý của đồ thị, đầu tiên ta nối s với đỉnh lân cận gần nó nhất, chẳng hạn là đỉnh y. Nghĩa là trong số các cạnh kề của đỉnh s, cạnh (s, y) có độ dài nhỏ nhất. Tiếp theo, trong số các cạnh kề với 2 đỉnh s hoặc y ta tìm cạnh có độ dài nhỏ nhất, cạnh này dẫn đến đỉnh thứ ba z, và ta thu được cây bộ phận gồm 3 đỉnh và 2 cạnh. Quá trình này sẽ được tiếp tục cho tới khi ta thu được được cây gồm n đỉnh và n - 1 cạnh, cây đó cũng là cây bao trùm nhỏ nhất cần tìm. Các bước thuật toán như sau:
- Bước 1: Chọn u1 sao cho l(u1) = min{l(u)} với u U đặt U1 = {u1}
- Bước 2: Chọn u2 sao cho l(u2) = min{l(u)} với u U\U1 và u kề với 1 cạnh thuộc U1
...
- Bước k +1: Giả sử đã có Uk = {u1, u2, ..., uk}
Chọn uk + 1 sao cho l(uk+1) = min{l(u)} với u U\Uk và u kề với 1 cạnh thuộc Uk.
Với thuật toán Prim, cạnh mới được chọn cũng phải không lập thành chu trình với các cạnh đã chọn ở các bước trước.
Thuật toán dừng ở bước n - 1.
Kết luận: cây T = <X, Un-1> là cây bao trùm ngắn nhất của G.
* Về hình thức thuật toán Prim phức tạp hơn thuật toán Kruskal nhưng về khối lượng tính toán sẽ giảm đi rất nhiều lần. Bởi vì thuật toán Prim chỉ cần xét các cạnh kề với các cạnh đã được chọn, còn trong thuật toán Kruskal phải xem xét tất cả các cạnh còn lại của đồ thị. Chính vì thế thuật toán Prim tỏ ra hiệu quả hơn, còn thuật toán Kruskal phải tính toán nhiều nên làm việc kém hiệu quả đối với những đồ thị có số cạnh lớn.
3.2.3 Chương trình thể hiện thuật toán.
Chương trình Pascal thể hiện thuật toán Kruskal tìm cây bao trùm nhỏ nhất
Uses crt; Type
Arrn = Array[1..50] of integer; Arrm = Array[1..5000] of Integer; Var n, m, MinL : Integer;
Dau, Cuoi, W: Arrm; DauT, CuoiT, Father: Arrn; Connect: Boolean;
Procedure NhapDl; Var i: Integer; FName: String; f: Text;
Begin
Write('Cho ten file du lieu: '); Readln(Fname); Assign(f, Fname); Reset(f);
Readln(f,n,m);
For i:=1 to m do Readln(f,dau[i],Cuoi[i],W[i]); Close(f);
End;
Procedure Indulieu; Var i: Integer; Begin
Writeln('So dinh: ',n,' So canh: ',m); Writeln('Dinh dau Dinh cuoi Do dai');
For i:=1 to m do Writeln(Dau[i]:4, Cuoi[i]:10, W[i]:12); End;
Procedure Heap(First, Last: Integer); Var j, k ,t1, t2, t3: Integer;
Begin j:= First;
While (j<=Trunc(last/2)) do Begin
If (2*j < Last) and (W[2*j + 1] < W[2*j]) then k:= 2*j + 1
Else k:= 2*j;
If W[k]<W[j] then Begin
t1:= Dau[j]; t2:=Cuoi[j]; t3:=W[j];
Dau[j] := Dau[k]; Cuoi[j]:=Cuoi[k]; W[j]:=W[k]; Dau[k]:=t1; Cuoi[k]:=t2; W[k]:=t3; j:=k; End Else j:=Last; End; End;
Function Find(i: Integer): Integer; Var Tro: Integer;
Tro:=i;
While Father[Tro]>0 do Tro:=Father[Tro]; Find:= Tro;
End;
Procedure Union(i, j : Integer); Var x: Integer;
Begin
x:=Father[i] + Father[j];
If Father[i]>Father[j] then Begin Father[i]:=j; Father[j]:=x; End Else Begin Father[j]:=i; Father[i]:=x; End; End; Procedure Kruskal;
Var i,Last, u, v, r1, r2, Ncanh, Ndinh: Integer; Begin
{Khoi tao mang Father danh dau cay con va khoi tao Heap} For i:=1 to n do Father[i]:=-1;
For i:=Trunc(m/2) downto 1 do Heap(i,m); Last:=m; Ncanh:=0; Ndinh:=0;
MinL :=0; Connect := True;
While (Ndinh< n - 1) and (Ncanh<m) do Begin NCanh := NCanh + 1;
u:= Dau[1]; v:= Cuoi[1];
{Kiem tra u va v co thuoc cung mot cay con} r1:= Find(u);
r2:= Find(v);
If r1<>r2 then Begin
{ket nap canh (u,v) vao cay khung} Ndinh := Ndinh + 1; Union(r1,r2); DauT[Ndinh]:=u;
CuoiT[Ndinh]:=v;
MinL := Minl + W[1]; End;
{To chuc lai Heap} Dau[1]:= Dau[Last]; Cuoi[1]:= Cuoi[Last]; W[1]:=W[Last]; Last := Last - 1; Heap(1,Last); End;
If Ndinh <> n - 1 then Connect:= False; End;
Procedure InKetQua; Var i: Integer;
Begin
Writeln('Do dai cua cay khung nho nhat: ',MinL); Writeln('Cac canh cua cay khung nho nhat: ');
For i:=1 to n - 1 do Writeln('(',DauT[i]:2,', ',CuoiT[i]:2,')'); End;
BEGIN
NhapDl; InDuLieu; KrusKal;
If Connect then InKetQua
Else Writeln('Do thi khong lien thong'); END.
3.3 Ứng dụng cho bài toán kết nối hệ thống mạng
Hình 2.3 0 1 2 4 5 3 6 7 8 5 2 8 9 6 11 3 7 3 4 6 9
Xét 1 ứng dụng của đồ thị trong bài toán nối mạng máy tính: Cần nối mạng 1 hệ thống mạng gồm nhiều máy tính, kết nối làm sao giữa 2 máy bất kỳ đều có 1 đường thông tin qua lại, cho biết chi phí kết nối giữa các máy hãy tìm cách kết nối sao có tổng chi phí là thấp nhất. Để giải bài toán này, có thể mô hình bằng đồ thị trong đó mỗi máy là 1 đỉnh, các cạnh của đồ thị là tất cả các khả năng có thể kết nối giữa các máy, trên mỗi cạnh là trọng số tương ứng với chi phí kết nối. Rõ ràng đây là bài toán tìm cây bao trùm ngắn nhất của đồ thị. Giả sử có 1 hệ thống mạng như hình 2.3, cây bao trùm nhỏ nhất có những cạnh được tô đậm.