Minimum yayılan ağaç: Prim, Kruskal ve union-find
Ağırlıklı yönsüz grafta tüm düğümleri en ucuza bağlayan ağaç. Kruskal'ı union-find ile, Prim'i adım tablosuyla izlemeyi ve quiz tipi soruları çözmeyi öğrenirsin.
Bir kampüse kablo çekiyorsun. Her bina çifti arasında bir maliyet var. Bütün binaları birbirine bağlamak istiyorsun ama en az kabloyla. Cevap minimum yayılan ağaçtır (minimum spanning tree, MST): tüm düğümleri içeren, döngüsüz, toplam ağırlığı en küçük kenar kümesi. Bu haftanın iki algoritması da greedy’dir. Sınav ve quiz soruları neredeyse hep iz sorusudur: “Kruskal’ın kabul ettiği kenarları sırayla yaz”, “Prim’i A’dan başlat, sırayı ver”, “MST ağırlığı kaç?”, “union-find dizisini göster”.
Altın kurallar:
- MST’de tam V − 1 kenar vardır. Daha azı bağlamaz, daha fazlası döngü kurar.
- Kruskal kenar seçer: en hafif kenardan başla, döngü kurmuyorsa al.
- Prim düğüm büyütür: tek ağaç, her adımda ona en ucuz bağlanan düğümü ekle.
- Prim’in tablosu Dijkstra’ya benzer ama yol toplamı tutmaz, sadece tek kenarın ağırlığını tutar.
- Döngü kontrolü union-find ile yapılır: iki uç aynı kümedeyse red.
Tanım ve iki özellik
Yayılan ağaç (spanning tree): bağlı, yönsüz bir grafın tüm düğümlerini içeren ve döngüsü olmayan alt grafı. V düğüm için V−1 kenar. Minimum olanı, kenar ağırlıkları toplamı en küçük olandır.
Bütün MST algoritmalarının doğruluğu iki özelliğe dayanır:
- Kesim özelliği (cut property): Düğümleri herhangi iki gruba ayır. İki grup arasında geçen kenarlardan en hafifi (eşsizse) her MST’de yer alır.
- Döngü özelliği (cycle property): Bir döngüdeki en ağır kenar (eşsizse) hiçbir MST’de yer almaz.
Prim kesim özelliğini doğrudan kullanır (kesim = ağaç / geri kalanı). Kruskal, reddettiği her kenarın bir döngünün en ağır kenarı olduğunu kullanır.
Çalışma grafı
G grafı (yönsüz). Kenarlar ağırlığa göre sıralı:
kenar ağırlık kenar ağırlık
A-C 1 B-C 7
C-D 2 C-E 8
A-B 3 D-F 9
D-E 4 B-E 10
B-D 5
E-F 6
Kruskal algoritması
- Kenarları ağırlığa göre artan sırala (eşitlikte listeleme sırası).
- Her düğüm kendi kümesinde başlar.
- Sıradaki kenar (u, v):
find(u) != find(v)ise kenarı al veunion(u, v). Değilse reddet (döngü kurar). - V−1 kenar alınca dur.
kenar karar kümeler sonrası
A-C 1 KABUL {A,C} {B} {D} {E} {F}
C-D 2 KABUL {A,C,D} {B} {E} {F}
A-B 3 KABUL {A,B,C,D} {E} {F}
D-E 4 KABUL {A,B,C,D,E} {F}
B-D 5 RED B ve D aynı kümede
E-F 6 KABUL {A,B,C,D,E,F} → 5 kenar, dur
Programın parent dizisi (A..F sırasıyla, her eleman o düğümün ebeveyni):
A-C 1 ACCEPT parent [A B A D E F]
C-D 2 ACCEPT parent [A B A A E F]
A-B 3 ACCEPT parent [A A A A E F]
D-E 4 ACCEPT parent [A A A A A F]
B-D 5 reject parent [A A A A A F]
E-F 6 ACCEPT parent [A A A A A A]
Kruskal total 16 edges 5
MST:
B
| 3
A
| 1
C
| 2
D
| 4
E
| 6
F
Bu örnekte MST bir zincir çıktı; her zaman böyle olmaz.
TUZAK · kalan kenarları da kontrol etmeye çalışmak. 5. kenar alındığında dur. B-C 7, C-E 8 gibi kenarlar zaten döngü kurar; sınavda tabloyu gereksiz uzatıp zaman kaybetme. Ama soru “list all rejected edges” diyorsa kalanları da red olarak yaz.
Union-find (disjoint set)
Kruskal’ın döngü kontrolü bu yapıyla hızlanır. Her küme bir ağaçtır; parent[x] x’in ebeveynidir, kökte parent[r] = r.
- find(x): köke kadar çık, kökü döndür.
- union(a, b): iki kökü bul, birini diğerinin altına bağla.
İki iyileştirme:
- Union by size (ya da rank): küçük ağacın kökünü büyüğün altına bağla. Ağaç yüksekliği ‘i geçmez.
- Path compression: find sırasında yoldaki her düğümü doğrudan köke bağla. Sonraki find’lar tek adım.
İkisi birlikte işlem başına pratikte sabit süre verir (, ters Ackermann).
İz: 0..7 üzerinde union by size
Kural: boyutlar eşitse ikinci argümanın kökü birincinin altına girer.
union(0,1) parent [0, 0, 2, 3, 4, 5, 6, 7]
union(2,3) parent [0, 0, 2, 2, 4, 5, 6, 7]
union(4,5) parent [0, 0, 2, 2, 4, 4, 6, 7]
union(6,7) parent [0, 0, 2, 2, 4, 4, 6, 6]
union(0,2) parent [0, 0, 0, 2, 4, 4, 6, 6]
union(4,6) parent [0, 0, 0, 2, 4, 4, 4, 6]
union(0,4) parent [0, 0, 0, 2, 0, 4, 4, 6]
önce find(7) sonrası
0 0
/ | \ / / | \ \
1 2 4 1 2 4 6 7
| | \ | |
3 5 6 3 5
|
7
path from 7 before find: 7 6 4 0
find(7) = 0 parent after [0, 0, 0, 2, 0, 4, 0, 0]
TUZAK · path compression sadece yoldakileri değiştirir. find(7) sonrası 6 ve 7 köke bağlandı. 3 ve 5 yolda değildi, ebeveynleri (2 ve 4) değişmedi.
Prim algoritması
- Başlangıç düğümünün değeri 0, diğerleri ∞.
- Ağaçta olmayanlar içinden değeri en küçük düğümü seç, ağaca ekle (eşitlikte küçük etiket).
- Yeni düğümün her komşusu u için:
w(v,u) < d[u]ised[u] = w(v,u),from[u] = v. - Tüm düğümler eklenene kadar tekrar et.
Dijkstra’dan tek fark 3. adımda: Dijkstra d[v] + w karşılaştırır, Prim sadece w. Prim’in d[u] değeri “u’yu ağaca bağlamanın en ucuz kenarı”dır, kaynaktan uzaklık değil.
G’de A’dan başla (* ağaçta):
ekle A B C D E F kenar
A 0* 3 1 ∞ ∞ ∞ -
C 0* 3 1* 2 8 ∞ A-C 1
D 0* 3 1* 2* 4 9 C-D 2
B 0* 3* 1* 2* 4 9 A-B 3
E 0* 3* 1* 2* 4* 6 D-E 4
F 0* 3* 1* 2* 4* 6* E-F 6
Prim order A C(via A-C 1) D(via C-D 2) B(via A-B 3) E(via D-E 4) F(via E-F 6) total 16
Aynı MST, farklı ekleme sırası. F’den başlasaydın sıra F, E, D, C, A, B olurdu ama kenarlar ve toplam 16 yine aynı.
İPUCU · Prim mi Kruskal mı? Seyrek grafta ve kenar listesi verildiğinde Kruskal pratiktir. Yoğun grafta (komşuluk matrisi) Prim’in sürümü iyidir. Sınavda hangisi istenirse onun sırasını yaz; ikisini karıştırma.
MST tek mi?
Bütün kenar ağırlıkları farklıysa MST tektir. Eşit ağırlıklar varsa birden fazla MST olabilir, ama hepsinin toplamı aynıdır.
H grafı: P-Q 2, P-R 3, Q-R 2, Q-S 4, R-S 4, R-T 5, S-T 1.
S-T 1 ACCEPT
P-Q 2 ACCEPT
Q-R 2 ACCEPT
P-R 3 reject
Q-S 4 ACCEPT
Kruskal total 9 edges 4
Q-S yerine R-S (o da 4) alınsaydı da {P, Q, R} ile {S, T} bağlanırdı ve toplam yine 9 olurdu. İki geçerli MST var.
Quiz provası
Quiz’de genelde 15-20 dakikada tek bir graf verilir ve üç şey istenir. Kendini zamanla dene (G grafı):
1) Kruskal'ın kabul ettiği kenarlar sırayla?
2) Prim B'den başlarsa ekleme sırası?
3) MST ağırlığı?
cevaplar:
1) A-C, C-D, A-B, D-E, E-F
2) B, A(3), C(1), D(2), E(4), F(6)
3) 16
- cevabın kontrolü: B’den A=3, D=5, C=7, E=10. A eklenir, C=1 olur. C eklenir, D=2, E=8. D eklenir, E=4, F=9. E eklenir, F=6. Kâğıda tabloyu çizmeden yapma; ezbere giden en sık hata, B’den başlayınca D=5’i seçmek.
Karmaşıklık özeti
| Algoritma | Veri yapısı | Süre |
|---|---|---|
| Kruskal | sıralama + union-find | |
| Prim | binary heap | |
| Prim | dizi taraması | |
| union-find | size + compression | işlem başına |
Sık yapılan hatalar
| Yanlış düşünce | Doğrusu |
|---|---|
| Prim’de d[u] = d[v] + w | Prim’de d[u] = w(v,u); toplam yol Dijkstra’dır |
| MST = kaynaktan en kısa yollar ağacı | Farklı şeyler; MST toplam ağırlığı, Dijkstra her yolu küçültür |
| Kruskal her adımda bağlı bir ağaç tutar | Kruskal orman tutar, en sonda birleşir; tek ağaç Prim’dir |
| Prim’de son eklenenin komşusunu seç | Ağaçtaki TÜM düğümlerin dışarı kenarlarından en hafifi |
| Union’da a’yı doğrudan b’ye bağla | KÖKLERİ bağla: parent[find(b)] = find(a) |
| Eşit ağırlıkta farklı MST = yanlış | Toplam aynıysa geçerli |
| MST’de V kenar | V − 1 kenar |
Kendini test et
Sınavdaki gibi İngilizce sorular. Önce kâğıt kalemle çöz, sonra cevabı aç.
Graph G (undirected): A-B 3, A-C 1, B-C 7, B-D 5, C-D 2, C-E 8, D-E 4, D-F 9, E-F 6, B-E 10. How many edges does its MST have, and what is the total weight?
Cevabı göster
6 düğümlü ağaçta V-1 = 5 kenar olur. Kruskal A-C 1, C-D 2, A-B 3, D-E 4, E-F 6 alır: 1+2+3+4+6 = 16.
Run Kruskal's algorithm on G. In which order are edges accepted?
Cevabı göster
Kenarları ağırlığa göre sırala: A-C 1, C-D 2, A-B 3, D-E 4, B-D 5, E-F 6, B-C 7, C-E 8, D-F 9, B-E 10.
A-C, C-D, A-B, D-E kabul. B-D 5: B ve D zaten aynı kümede (A-B ve A-C-D üzerinden), red. E-F 6 kabul. 5 kenar oldu, dur.
Which edge is the first one Kruskal rejects on G, and why?
Cevabı göster
B-D geldiğinde find(B) = find(D) = A. Bu kenar B-A-C-D-B döngüsünü kapatırdı. Döngü özelliği (cycle property): bir döngünün en ağır kenarı (burada B-D 5 > 3, 1, 2) MST'de olmak zorunda değildir.
Run Prim's algorithm on G starting from A. In which order are vertices added, and through which edge?
Cevabı göster
ekle A B C D E F
A 0* 3 1 ∞ ∞ ∞
C 0* 3 1* 2 8 ∞
D 0* 3 1* 2* 4 9
B 0* 3* 1* 2* 4 9
E 0* 3* 1* 2* 4* 6
F 0* 3* 1* 2* 4* 6*Her adımda ağaca en ucuz bağlanan düğüm seçilir. D eklenince B=3 ile E=4 yarışır; B kazanır.Run Prim on G starting from F. Give the order of added vertices.
Cevabı göster
F: E=6, D=9. E eklenir (6): D=4, C=8, B=10. D eklenir (4): C=2, B=5. C eklenir (2): A=1. A eklenir (1): B=3. B eklenir (3).
Kenarlar F-E 6, E-D 4, D-C 2, C-A 1, A-B 3: yine 16. Başlangıç düğümü sırayı değiştirir, ağırlıkları farklı olan grafta MST'yi değiştirmez.
Cut property: in G, consider the cut S = {A, B} versus the rest {C, D, E, F}. Which edges cross the cut, and which one must be in every MST?
Cevabı göster
Kesimi geçen kenarlar bir ucu S'de, diğeri dışarıda olanlar. Kesim özelliği (cut property): kesimi geçen en hafif kenar (benzersizse) her MST'dedir. Prim de her adımda 'ağaç' ile 'geri kalanı' arasındaki kesimin en hafif kenarını alır.
Union-find on 0..7 with union by size (on equal size, the second root goes under the first) and path compression. Perform union(0,1), union(2,3), union(4,5), union(6,7), union(0,2), union(4,6), union(0,4). Give the parent array.
Cevabı göster
İlk dört union: 1→0, 3→2, 5→4, 7→6. union(0,2): iki kök de boy 2, 2→0. union(4,6): 6→4. union(0,4): boyutlar 4 ve 4, 4→0.
0
/ | \
1 2 4
| | \
3 5 6
|
7Bu union'larda find çağrıları zaten köklerde olduğu için sıkıştırma bir şey değiştirmedi.Continuing from the previous state, call find(7) with path compression. What does it return, and what is the parent array afterwards?
Cevabı göster
Yol 7 → 6 → 4 → 0. Path compression yoldaki her düğümü doğrudan köke bağlar: parent[7] = 0, parent[6] = 0 (4 zaten 0'a bağlı). Bir sonraki find(7) tek adımda biter.
Graph H: P-Q 2, P-R 3, Q-R 2, Q-S 4, R-S 4, R-T 5, S-T 1. What is the MST weight? Is the MST unique?
Cevabı göster
Kruskal (eşitlikte listeleme sırası): S-T 1, P-Q 2, Q-R 2 kabul; P-R 3 red (döngü); Q-S 4 kabul. Toplam 1+2+2+4 = 9.
R-S 4 listede önce olsaydı Q-S yerine o alınırdı; yine 9. İki farklı MST, aynı ağırlık. Tüm kenar ağırlıkları farklıysa MST tektir; eşit ağırlık varsa birden fazla olabilir.
Give the running times: (a) Kruskal with sorting and union-find, (b) Prim with a binary heap, (c) Prim with an adjacency matrix and array scan.
Cevabı göster
(a) Sıralama baskın: ; olduğu için , yani . Union-find işlemleri neredeyse sabit. (b) Dijkstra ile aynı yapı. (c) Her adımda V eleman taranır, V adım: yoğun graf için iyi.
Write int find(int x) with path compression and boolean union(int a, int b) with union by size, using arrays parent[] and size[]. union returns false if a and b are already in the same set.
Cevabı göster
static int[] parent, size;
static int find(int x) {
if (parent[x] != x)
parent[x] = find(parent[x]); // path compression
return parent[x];
}
static boolean union(int a, int b) {
int ra = find(a), rb = find(b);
if (ra == rb) return false; // aynı küme: döngü
if (size[ra] < size[rb]) { int t = ra; ra = rb; rb = t; }
parent[rb] = ra; // küçük, büyüğün altına
size[ra] += size[rb];
return true;
}Başlangıçta parent[i] = i, size[i] = 1. İkisi birlikte işlem başına neredeyse sabit (ters Ackermann) süre verir.Write int kruskal(int n, int[][] edges) that returns the MST weight. Each edge is {u, v, w}. Use the find/union methods from the previous question.
Cevabı göster
static int kruskal(int n, int[][] edges) {
parent = new int[n];
size = new int[n];
for (int i = 0; i < n; i++) { parent[i] = i; size[i] = 1; }
int[][] es = edges.clone();
Arrays.sort(es, (x, y) -> Integer.compare(x[2], y[2]));
int total = 0, used = 0;
for (int[] e : es) {
if (union(e[0], e[1])) {
total += e[2];
if (++used == n - 1) break;
}
}
return total;
}G grafında (A..F = 0..5) 16 döner. Graf bağlı değilse used < n-1 kalır; o zaman MST yoktur, sadece yayılan orman vardır.Doğrulama: Kruskal kabul/ret sırası ve her adımdaki parent dizisi, Prim adım tabloları (A'dan ve F'den), ikinci graftaki eşit ağırlıklı alternatif MST, union by size + path compression izi verify/SEN2212/W13.java ile çalıştırıldı (java W13.java). find/union ve kruskal metotları verify/SEN2212/WMethods11to14.java içinde test edildi.