CHƯƠNG 6
CÂY
Tôn Quang Toại Khoa CNTT, Đại học Ngoại ngữ ‐ Tin học TP.HCM
Nội dung
Khái niệm Cây Các tính chất cơ bản của Cây
Cây khung của đồ thị Cây khung nhỏ nhất
Khái niệm cây
Định nghĩa Cây (Tree): Cây là
Đồ̀ thị vô hướng, liên thông và không có chu trình
B E B E F F
A A
C D C D
G2
G1
Khái niệm cây
Định nghĩa Rừng (Forest): Rừng là đồ thị không có chu trình
B G I L
J C A F K
• Nhận xét: Rừng là đồ thị mà mỗi thành phần
liên thông của nó là một cây.
D E H
Các tính chất cơ bản của Cây
Định lý: Cho đồ̀ thị vô hướng G=(V, E) có n đỉnh. Các mệnh đề̀ sau tương đương: (1) G là 1 cây (2) G không chứa chu trình và có n-1 cạnh (3) G liên thông và có n-1 cạnh (4) G liên thông và mỗi cạnh của nó điều là cầu (5) Giữa hai đỉnh bất kỳ của G được nối với nhau bởi đúng một đường đi duy nhất (6) G không chứa chu trình nhưng khi thêm vào một cạnh ta thu được đúng một chu trình
Các tính chất cơ bản của Cây
Sơ đồ chứng minh:
Chứng minh:
:
Các tính chất cơ bản của Cây
Chứng minh
Các tính chất cơ bản của Cây
Chứng minh
Các tính chất cơ bản của Cây
Chứng minh
Các tính chất cơ bản của Cây
Chứng minh
Các tính chất cơ bản của Cây
Chứng minh
Cây khung của đồ thị
D C
Định nghĩa Cây khung (spanning tree) của đồ̀ thị: Một cây T gọi là cây khung (cây bao trùm) của đồ thị G=(V, E) nếu
T = (V,E’) E’ E
F A
C D B E
C D
F A
F A
B E
B E
Cây khung của đồ thị
Định lý Cayley
Số cây khung của đồ thị (cid:3041) là (cid:3041)(cid:2879)(cid:2870)
a
n=4, số cây khung là: 4(cid:2872)(cid:2879)(cid:2870) (cid:3404) 16
f
b
d
c
Ví dụ: abc, bcd, cda, dab abf, acf, bdf, ...
e
n=5, Số cây khung là: 5(cid:2873)(cid:2879)(cid:2870) (cid:3404) 125
A
C B
E D
Cây khung của đồ thị
Bài toán: Cho G là đồ thị vô hướng, liên thông, hãy tìm 1 cây khung của đồ thị
Lời giải
Thuật toán tìm kiếm theo chiều sâu (DFS) Thuật toán tìm kiếm theo chiều rộng (BFS)
Cây khung của đồ thị
Tìm cây khung theo DFS
void SpanningTree_DFS(int u) {
1. Đánh dấu u đã viếng thăm 2. Xét mọi đỉnh v chưa được viếng thăm và kề u, với
từng đỉnh v đó
{
T = T {(u, v)} DFS(v);
}
}
Cây khung của đồ thị
Tìm cây khung theo BFS
void SpanningTree_BFS() {
<1. Khởi tạo>
- Đánh dấu mọi đỉnh chưa viếng thăm - Đánh dấu u=0 đã viếng thăm - q.Enqueue(u)
<2. Lặp> while (q!=) {
- u = q.Dequeue(); - Tìm tất cả đỉnh v chưa viếng thăm và kề u {
+ T = T {(u, v)} + Thăm đỉnh v + q.Enqueue(v)
}
}
}
Cây khung nhỏ nhất
Phát biểu bài toán Cây khung nhỏ nhất (Minimum Spanning Tree – MST):
, đạt giá trị nhỏ nhất
Cho G=(V, E) là đồ thị vô hướng, liên thông và có trọng số. Hãy tìm 1 cây khung T=(V, E’) của đồ thị G sao cho tổng trọng số các cạnh của T là nhỏ nhất. Hay nói cách khác: Tìm E’ sao cho độ dài của cây T,
𝑤 𝑇 (cid:3404) (cid:3533) 𝑤(cid:4666)𝑢, 𝑣(cid:4667) (cid:4666)(cid:3048),(cid:3049)(cid:4667)∈(cid:3006)
Cây khung nhỏ nhất
Thuật toán tìm cây khung nhỏ nhất
Có nhiều thuật toán xây dựng cây khung nhỏ nhất: • Thuật toán Boruvka • Thuật toán Kruskal • Thuật toán Jarnik – Prim • Phương pháp Dijkstra • Thuật toán Cheriton – Tarjan • Thuật toán Chazelle • …
Cây khung nhỏ nhất
Thuật toán tìm cây khung nhỏ nhất
Thuật toán Kruskal: Ý tưởng của thuật toán Kruskal: • Ban đầu cây T=(V, Ø) (Cây rỗng) • Lần lược chọn đủ (n‐1) cạnh có trọng số từ nhỏ đến lớn
vào cây T sao cho không được tạo thành chu trình
Thuật toán Jarnik – Prim: Ý tưởng của thuật toán Jarnik – Prim: • Ban đầu cây T chỉ có 1 đỉnh. • Lần lược chọn thêm (n‐1) đỉnh vào cây T sao cho đỉnh
được chọn vào cây có cạnh đến một trong các đỉnh của cây là là nhỏ nhất
Cây khung nhỏ nhất
Thuật toán Kruskal Input: G=(V, E) Output: Danh sách cạnh cay[]
Bước 1 (Sắp xếp) Sắp xếp các cạnh có trọng số tang dần
Bước 2 (Chọn n‐1 cạnh) Xét các cạnh theo thứ tự đã sắp xếp (cạnh nhỏ chọn trước). Chọn (n-1) cạnh theo quy tắc:
• Cạnh được chọn và cùng các cạnh đã chọn trước đó
không tạo thành chu trình
Cây khung nhỏ nhất
Ví dụ: Dùng thuật toán Kruskal để (cid:416)m cây khung nhỏ nhất của đồ thị sau:
7
H
C
6
6
G
2
6
9
3
B
8
6
2
F K D
1
5
4
3
9
A
5
J
6
5
7
E I
4
Cây khung nhỏ nhất
Cấu trúc dữ liệu
LinkedList> edgeList;
int m;
int n;
LinkedList> spanningTree;
bool[,] connected;
connected[u][v] cho biết đỉnh u có liên thông với v không: • connected[u][v] = true nếu u và v liên thông với nhau
v
u
Cây khung nhỏ nhất
Vấn đề: Chúng ta phải cập nhật bảng connected[][] này như thế nào?
v
u
y
x
Lúc u và v chưa nối thì {x} và {y} chưa liên thông. Nếu nối u với v thì phải cập nhật các đỉnh {x} liên thông với các đỉnh {y}
– Chúng ta dùng for toàn bộ bên x và bên y để cập nhật mảng
connected[][]
Cây khung nhỏ nhất
Thuật toán Kruskal tóm tắt
Bước 1 (Sắp xếp) Sắp xếp các cạnh theo thứ tự tăng dần về trọng số
Bước 2: Lặp (n‐1) lần Xét các cạnh theo thứ tự đã sắp xếp (cạnh nhỏ xét trước). • [Tìm cạnh]: Tìm cạnh (u, v) không liên thông đưa vào T • [Cập nhật]: Cập nhật 2 miền liên thông chứa đỉnh {u} và
{v} tương ứng thành 1 miền
Cây khung nhỏ nhất
void Kruskal() {
}
Cây khung nhỏ nhất
Một số cách kiểm tra 2 đỉnh có liên thông hay không:
Dùng mảng 2 chiều: int connected[][]; connected[i][j] = true nếu i liên thông j (=0 ngược lại)
Dùng mảng 1 chiều: int connected[]; connected[i] = connected[j] thì i liên thông j
Dùng Union‐Find set
Cây khung nhỏ nhất
Thuật toán Jarnik – Prim
Bước 1: Chọn đỉnh x0 bất kỳ và đánh dấu x0
Bước 2: Lặp (n-1) lần • Chọn cạnh (x, y) thỏa điều kiện:
– x đã đánh dấu – y chưa đánh dấu – Độ dài cạnh (x, y) ngắn nhất
• Đánh dấu y
Cây khung nhỏ nhất
Thuật toán Jarnik – Prim
Bước 1: Chọn đỉnh x0 bất kỳ và và đưa vào cây (đánh dấu x0)
Bước 2: Lần lượt nạp (n-1) đỉnh còn lại (tương ứng n‐1 cạnh ) vào cây bằng cách: • Chọn cạnh (x, y) thỏa điều kiện:
– x thuộc cây (x đã đánh dấu) – y chưa thuộc cây (y chưa đánh dấu) – Độ dài cạnh (x, y) nhỏ nhất
• Đánh dấu y
Cây khung nhỏ nhất
Ví dụ: Dùng thuật toán Prim để (cid:416)m cây khung nhỏ nhất của đồ thị sau:
7
H
C
6
6
G
2
6
9
3
B
8
6
2
F K D
1
5
4
3
9
A
5
J
6
5
7
E I
4
Cây khung nhỏ nhất
void Jarnik_Prim() {
}
Cây khung nhỏ nhất
Bài toán “Tìm cây khung lớn nhất”
Cách 1: • Đổi dấu các cạnh của đồ thị • Áp dụng thuật toán Kruskal hay Prim
Cách 2: Chỉnh sửa thuật toán Kruskal hay Prim bằng cách Chọn cạnh lớn trước, cạnh nhỏ sau