Định nghĩa : Chu trình đơn trong đồ thị G đi qua mỗi cạnh của nó một lần
được gọi là chu trình Euler. Đường đi đơn trong G đi qua mỗi cạnh của nó một lần được gọi là đường đi Euler. Đồ thị được gọi là đồ thị Euler nếu nó có chu trình Euler, và gọi là đồ thị nửa Euler nếu nó có đường đi Euler.
Rõ ràng mọi đồ thị Euler luôn là nửa Euler, nhưng điều ngược lại không luôn đúng
Ví dụ 1. Đồ thị G1 trong hình 1 là đồ thị Euler vì nó có chu trình Euler a, e, c, d, e, b, a. Đồ thị G3 không có chu trình Euler nhưng nó có đường đi Euler a, c, d, e, b, d, a, b, vì thế G3 là đồ thị cửa Euler. Đồ thị G2 không có chu trình cũng như đường đi Euler.
Hình 1. Đồ thị G1, G2, G3
Ví dụ 2. Đồ thị H2 trong hình 2 là đồ thị Euler vì nó có chu trình Euler a, b, c, d, e, a. Đồ thị H3 không có chu trình Euler nhưng nó có đường đi Euler c, a, b, c, d, b vì thế H3 là đồ thị nửa Euler. Đồ thị H1 không có chu trình cũng như đường đi Euler.
Hình 2: Đồ thị H1, H2, H3
Điều kiện cần và đủ để một đồ thị là một đồ thị Euler, được Euler tìm ra vào năm 1736 khi ông giải quyết bài toán hóc búa nổi tiếng thế giới thời đó về bảy cái cầu ở thành phố Konigsberg và đây là định lý đầu tiên của lý thuyết đồ thị.
Định lý (Euler): Đồ thị vô hướng liên thông G là đồ thị Euler khi và chỉ khi
mọi đỉnh của G đều có bậc chẵn.
Để chứng minh định lý trước hết ta chứng minh bổ để:
Bổ đề: Nếu bậc của mỗi đỉnh của đồ thị G không nhỏ hơn 2 thì G chứa chu
trình.
Chứng minh.
Nếu G có cạnh lặp thì khẳng định của bồ đề là hiển nhiên. Vì vậy giả sử G là đơn đồ thị. Gọi v là một đỉnh nào đó của G. Ta sẽ xây dựng theo qui nạp đường đi
V V1 V2 …
trong đó V1 là đỉnh kề với v, còn với i≥1 chọn Vi+1 # Vi-l (có thể chọn Vi+1 như vậy là vì deg(Vi) ≥2). Do tập đỉnh của G là hữu hạn , nên sau một số hữu hạn bước ta phải quay lại một đỉnh đã xuất hiện trước đó. Gọi đỉnh đầu tiên như thế là vk. Khi đó, đoạn của đường đi xây dựng nằm giữa hai đỉnh vk là 1 chu trình cần tìm.
Chứng minh định lý:
Cần. Giả sử G là đồ thị Euler tức là tồn tại chu trình Euler P trong G. Khi đó
cứ mỗi lần chu trình P đi qua một đỉnh nào đó của G bậc của đỉnh đó tăng lên 2. mặt khác mỗi cạnh của đồ thị xuất hiện trong P đúng một lần, suy ra mỗi đỉnh của đồ thị điều có bậc chẵn.
Đủ. Quy nạp theo số đỉnh và số cạnh của G. Do G liên thông và deg(v) là
số chẵn nên bậc của mỗi đỉnh của nó không nhỏ hơn 2. Từ đó theo bổ đề G phải chứa chu trình C. Nếu C đi qua tất cả các cạnh của G thì nó chính là chu trình Euler. Giả sử C không đi qua tất cả các cạnh của G. Khi đó loại bỏ khỏi G tất cả các cạnh thuộc C ta thu được một đồ thị mới H vẫn có bậc là chẵn. Theo giả thiết qui nạp, trong mỗi thành phần liên thông của H điều tìm được
chu trình Euler. Do G là liên thông nên trong mỗi thành phần của H có ít nhất một đỉnh chung với chu trình C. Vì vậy, ta có thể xây dựng chu trình Euler trong G như sau: bắt đầu từ một đỉnh nào đó của chu trình C, đi theo các cạnh của C chừng nào chưa gặp phải đỉnh không cô lập của H. Nếu gặp phải đỉnh như vậy ta sẽ đi theo chu trình Euler của thành phần liên thông của H chứa đỉnh đó. Sau đó lại tiếp tục đi theo cạnh của C cho đến khi gặp phải đỉnh không cô lập của H thì lại theo chu trình Euler của thành phần liên thông tương ứng trong Hv.v… (xem hình 3). Quá trình sẽ kết thúc khi ta trở về đỉnh xuất phát , tức là thu được chu trình đi qua mỗi cạnh của đồ thị đúng một lần.
Hình 3. Minh hoạ cho chứng minh định lý 1.
Hệ quả: Đồ thị vô hướng liên thông G là nửa Euler khi và chỉ khi nó có
không quá 2 đỉnh bậc lẻ.
Chứng minh. Thực vậy , nếu G có không quá 2 đỉnh bậc lẻ thì số đỉnh bậc lẻ
của nó chỉ có thể là 0 hoặc 2. Nếu G không có đỉnh bậc lẻ thì theo định lý 1, nó là đồ thị Euler. Giả sử G có 2 đỉnh bậc lẻ là u và v. Gọi H là đồ thị thu được từ G bằng cách thêm vào G một đỉnh mới w và hai cạnh (w,u) và(w,v).
Khi đó tất cả các đỉnh của H điều có bậc chẵn, vì thế theo định lý 1, nó có chu trình Euler C. Xoá bỏ khỏi chu trình này đỉnh w và hai cạnh kề nó ta thu được đường đi Euler trong đồ thị G.
Giả sử G là đồ thị Euler, từ chứng minh định lý ta có thủ tục sau để tìm chu trình Euler trong G.
Procedure Euler_Cycle;
Begin
STACK:=φ ; CE:= φ ;
Chon u la mot dinh nao do cua do thi;
STACK ⇐ u;
While STACK<>φ do Begin
X:=top(STACK); (* x la phan tu dau STACK) If Ke(x)<> φ then
Begin
Y:=dinh dau tien trong danh sach Ke(x);
STACK ⇐ y;
(* loai bo canh (x,y) khoi do thi *) Ke(x):=Ke(x)\{ y} ;
End Else Begin x ⇐ STACK; CE ⇐ x; End; End; End;
3.6. Một số thuật toán tìm đường đi ngắn nhất giữa tất cả các đỉnh:
Bài toán: cho một đồ thị có trọng số (G,c). Hãy tìm đường đi ngắn nhất giữa tất
cả các cặp đỉnh.
Bài toán này thường gặp trong việc xây dựng bảng khoảng cách giữa các thành phố, bảng giá cước vận chuyển giữa các nhà ga...
Bài toán này có thể giải quyết bằng cách sử dụng thuật toán Dijkstra với mỗi đỉnh của đồ thị lần lượt là các đỉnh xuất phát.
•Thuật toán Dijkstra tìm đường đi ngắn nhất:
Năm 1959 E.W.Dijkstra đưa ra một thuật toán rất hiệu quả để giải bài toán đường đi ngắn nhất. Thuật toán thực hiện việc gán và giảm giá trị của nhãn l(i) tại mỗi đỉnh i của đồ thị G như sau:
Thuật toán :
1. Với đỉnh xuất phát a, gán nhãn l(a):=0.
2. Nếu có cạnh (i,j) mà đỉnh i đã được gán nhãn và đỉnh j chưa được gán nhãn hoặc đỉnh j đã được gán nhãn nhưng l(i)+c(i,j)<l(j) thì giảm nhãn l(j):=l(i)+c(i,j).
Định lý: Tại mỗi đỉnh b giá trị nhãn l(b) cuối cùng (nếu có) chính là độ dài của đường đi ngắn nhất từ đỉnh a đến đỉnh b.
Chứng minh:
Sau khi đã thực hiện xong thuật toán trên, nếu giá trị nhãn l(b) xác định thì ta có đường đi từ đỉnh a tới đỉnh b. Ta khôi phục đường đi từ a đến b như sau:
Xuất phát từ đỉnh b, tìm cạnh có đỉnh cuối là b và đỉnh đầu là i sao cho:
l(i) + c(i,b) = l(b).
Đỉnh i như thế chắc chắn phải tồn tại vì xảy ra đẳng thức ở lần gán hoặc giảm giá trị nhãn l(j) cuối cùng. Cứ tiếp tục như thế cho đến khi gặp đỉnh a.
Giả sử ta nhận được dãy các cạnh:
(a, a1) , (a1, a2) , ... , (ak-1, b) mà trên đó: l(a) + c(a,a1) = l(a1)
l(a1) + c(a1,a2) = l(a2) .. . .. . . . .. .. .. . . .. .. . . l(ak-1) + c(ak-1, b) = l(b).
Cộng từng vế và khử các giá trị chung ở cả hai vế ta có:
c(a,a1) + c(a1,a2) + ... + c(ak-1,b) = l(b).
Vậy giá trị nhãn l(b) chính là độ dài đường đi nói trên.
Bất kỳ đường đi nào khác từ đỉnh a đến đỉnh b cũng có các hệ thức tương tự nhưng có dấu ≥.
Vậy nhãn l(b) là độ dài của đường đi ngắn nhất. ??
Độ dài đường đi ngắn nhất từ đỉnh a đến đỉnh b là 5.
Để đơn giản việc tính toán, ta xây dựng ma trận trọng số C :
Khi đó, thuật toán Dijkstra được trình bày chi tiết hơn như sau: procedure DIJKSTRA(a) ; begin for j ∈V do begin L[j] := C[a, j] ; Truoc[j] := a end ; T := V \ {a} ; while T ≠≠∅do begin chọn đỉnh i ∈T mà L[i] = min {L[j] j ∈T} ; T := T \ {i} ; for j∈T do if L[j] > L[i] + C[i, j] then begin L[j] := L[i] + C[i, j] ;
Truoc[j] := i ; end ;
end ; end ;
Biến mảng Truoc dùng để khôi phục đường đi.
* Tuy nhiên bắng cách sử dụng thuật toán Dijkstra để tìm đường đi ngắn nhất của tất cả các cặp định ta có thể giải quyết trực tiếp bài toán nhờ thuật toán Floyd.
•Thuật toán Floyd tìm đường đi ngắn nhất:
Bài toán đặt ra là tính tất cả các D(i,j) là khoảng cách nhỏ nhất từ i đến j. Ta sử dụng ma trận Dn x n để tính độ dài đường đi ngắn nhất giữa tất cả các cặp đỉnh.
1. Bắt đầu gán D := C - ma trận trọng số.
2. Thực hiện n lần lặp trên D. Sau bước lặp thứ k, D[i,j] chứa độ dài đường đi ngắn nhất từ đỉnh i đến đỉnh j mà chỉ đi qua các đỉnh có chỉ số không vượt quá k. Vậy trong bước lặp thứ k ta thực hiện theo công thức sau đây: D(k)[i,j] := min (D(k-1)[i,j] , D(k-1)[i,k] + D(k-1)[k,j]) , với k = 1, 2, ... , n.
Tức là: nếu đường đi có từ i j dài hơn đường đi từ i k cộng với đường đi từ k j thì ta ghi nhận lại đường đi ngắn nhất (hiện có) là đường đi qua k.
for k:=1 to n do for i:=1 to n do for j:=1 to n do
D[i,j] := min ( c[i,j] , c[i,k] + c[k,j]);
Chứng minh: goi D[k,i,j] là đường đi ngắn nhất từ i đến j mà chỉ đi qua các
đỉnh trung gian thuộc tập {1,2,…,k}. Rõ ràng ban đầu, khi k=0 thì D[0,i,j]=D[i,j](đường đi trực tiếp).
- Không đi qua đỉnh k, tức là sử dụng các đỉnh trung gian từ 1 đến k-1 thì D[k,i,j]:=D[k-1,i,j];
- Đi qua đỉnh k, khi đó đường đi ngắn nhất từ i đến j sẽ là nối của hai đường, một đường từ i đến k, một từ k đến j, và các đường con này chỉ đi qua các đỉnh trung gian từ 1…k-1
D[k,i,j]:=min(D[k-1],i,k)+D[k-1,k,j])
Ví dụ: Giả sử ta có bản đồ giao thông sau đây:
Các kết quả tính toán:
Thuật toán Floyd:
Dữ liệu: Ma trận trọng số C của đồ thị.
Kết quả: Ma trận D cho biết khoảng cách của tất cả các cặp đỉnh.
BEGIN
for i := 1 to n do for j := 1 to n do
begin D[i,j] := C[i,j] ; TRUOC[i,j] := 0 end ; for k := 1 to n do
for j := 1 to n do
if D[i,k] + D[k,j] < D[i,j] then begin
D[i,j] := D[i,k] + D[k,j] ; TRUOC[i,j] := k
end ; END .
Nếu TRUOC[i,j] = 0 thì đưòng đi ngắn nhất từ đỉnh i đến đỉnh j chính là cạnh (i, j).
Để in ra các đỉnh trung gian trên đường đi ngắn nhất từ đỉnh i đến đỉnh j ta dùng thủ tục đệ quy sau đây:
procedure Duong_di( i, j ) ; begin k := TRUOC[i,j] ; if k = 0 then Exit ; Duong_di( i, k ) ; write( k ) ; Duong_di( k, j ) ; end ;
Chẳng hạn, ma trận TRUOC của ví dụ trên là:
Để xác định đường đi ngắn nhất từ đỉnh 1 đến đỉnh 2 ta lấy k=TRUOC[1,2]=3. Vậy đường đi ngắn nhất là: < 1, 3, 2 >.