Bölüm 17: Minimum Kapsayan Ağaçlar
Giriş
Öğrenme Çıktıları
Kapsayan ağacı, ağaç/ağaç dışı kenarları ve önce derinliğine/genişliğine kapsayan ağaçları açıklamak.
Bir çizgenin kapsayan ağaçlarını üretme yollarını ve eşbiçimlilik (izomorfizm) kavramını tartışmak.
Kenarları artan maliyetle seçip döngüyü union-find ile yakalayan Kruskal’ı C ile izlemek.
Tek öbeği büyüten Prim’i O(n²) dizilerle ve heap ile gerçeklemek.
O(e log e), O(n²), O(e log n) sınırlarını karşılaştırıp yoğun/seyrek çizgeye göre seçmek.
“Veri yapısını keşfetmek”: min-heap ve kümeler algoritmayı nasıl hızlandırır?
Kapsayan Ağaçlar
Gezinme Kenarları İkiye Ayırır
Bağlantılı bir çizgede DFS ya da BFS ile tüm düğümler ziyaret edilince kenarlar iki gruba ayrılır:
İşlenen, yeni bir düğüme götüren kenarlar (turuncu). Tam n − 1 tanedir.
İşlenmeyen kenarlar (kesikli). DFS’de geri (back), BFS’de çapraz (cross) kenar olarak da sınıflanır.
DFS: N = {(0,2), (1,4), (6,7)} · BFS: N = {(4,7), (5,7), (6,7)}
Kapsayan Ağaçlar
Tanım: Kapsayan Ağaç (Spanning Tree)
K4 tam çizgedir. a ve b farklı iki kapsayan ağaçtır (a bir yol, b bir yıldız); c, d, e ise a ile eşbiçimlidir: a ≡ c ≡ d ≡ e.
K4’ün 16 kapsayan ağacı vardır (Cayley: nⁿ⁻² = 4² = 16): 12 yol biçimli + 4 yıldız. Ağırlıklar devreye girince “kaç tane?” sorusu yeniden ele alınır.
Kapsayan Ağaçlar
Eşbiçimlilik, Çizim ve Düzlemsellik
Aynı çizge (K3,3), iki farklı çizim
- Eşbiçimliliği sınamak için DFS/BFS’den yararlanılır; ama genelde zor bir problemdir.
- Kenarları kesişmeden çizilebilen çizge düzlemseldir (planar). K3,3 düzlemsel değildir: en az bir kesişme gerekir.
- Devre tasarımında: K3,3 biçimli bir devre en az iki katman ister.
Kapsayan Ağaçlar
Soru: Kaç Farklı Kapsayan Ağaç Vardır?
e kenar arasından n − 1 kenar C(e, n − 1) biçimde seçilir; her seçimde döngü olup olmadığı denetlenir ve eşbiçimliler ayıklanır. Burada C(5, 3) = 10 seçimin 8’i ağaçtır.
Önce bir kapsayan ağaç bulunur (ör. DFS ile).
Ağaç dışı bir kenar eklenir: tam bir döngü oluşur.
Döngüdeki (A,B) çıkarılır → farklı bir kapsayan ağaç. Sonra geri eklenip bir sonraki çıkarılır.
Döngüdeki (B,C) çıkarılır → farklı bir kapsayan ağaç. Sonra geri eklenip bir sonraki çıkarılır.
Döngüdeki (C,D) çıkarılır → farklı bir kapsayan ağaç. Sonra geri eklenip bir sonraki çıkarılır.
Bütün ağaç dışı kenarlar için tekrarlanır. Bu çizgede 8 kapsayan ağaç var; ama yöntem uzun ve tekrar üretir.
Bu algoritma uzundur; bu yüzden tercih etmek mantıklı değildir. (Kesin sayı için Kirchhoff matris-ağaç teoremi kullanılır — ileri konu.)
Kapsayan Ağaçlar
Kapsayan Ağacın Özellikleri
BFS kapsayan ağacı: 8 düğüm, 7 kenar (turuncu); ağaç dışı 3 kenar kesikli.
Ağaç dışı (6,7) eklenirse tam bir döngü oluşur: 6-2-0-1-3-7-6. Ağaç, ağaç olmaktan çıkar.
Herhangi bir ağaç kenarı, ör. (0,2), silinirse ağaç ikiye bölünür: {2, 5, 6} kopar. Kapsayan ağaç “en az kenarlı” bağlantılı alt çizgedir.
- Tam n − 1 kenar, döngü yok, bağlantılı — üçünden ikisi üçüncüsünü gerektirir.
- Her düğüm çifti arasında tek yol vardır.
- Bir ağaç dışı kenar eklemek tek bir döngü oluşturur; döngüden herhangi bir kenarı çıkarmak yeni bir kapsayan ağaç verir (kapsayan ağaçları üretme yöntemi buna dayanır).
- Uygulama: ağdaki gereksiz bağlantıları atmak, yayın (broadcast) ağacı, elektrik devre denklemleri (Kirchhoff).
Minimum Maliyet
Minimum Maliyetli Kapsayan Ağaç (MST)
- Yönsüz çizgede kenarlar ağırlıklı olabilir: G(V, E) yerine G(V, E, w).
- Kapsayan ağaçlar içinde toplam kenar maliyeti en az olan(lar): minimum kapsayan ağaç.
- Ör. a: ağdaki kenar maliyetleri bilgisayarlar arası bağlantı maliyetidir.
- Ör. b: 7 köye elektrik/fiber optik: fizibilite hangi köyden hangisine kaç km kablo gerektiğini vermiş. Bağlantı olmayan köyler arası ∞ (yazılmıyor).
Örnek çizge
Minimum Maliyet
Algoritmalar ve Açgözlü (Greedy) Yaklaşım
Kruskal, Prim, Sollin (Borůvka); ayrıca reverse-delete. Bu bölümde ilk ikisi.
Geri izleme (backtracking), açgözlü (greedy), dinamik programlama…
Her adımda o an için en iyi karar verilir ve sonra değiştirilmez; geri dönmez.
1) Döngü oluşturan kenar alınmaz. 2) En az maliyetli tercih edilir. 3) n düğümde tam n − 1 kenar seçilir.
“Güzellik, fazlalıktan arınmışlıktır.” — Michelangelo
Kruskal
Kruskal Algoritması (1956)
T = {}; /* seçilen kenarlar */
while (|T| < n-1 && E ≠ Ø) {
E'den en az maliyetli (v,w) kenarını seç;
E = E - {(v,w)};
if ((v,w) T içinde döngü oluşturmuyorsa)
T = T ∪ {(v,w)};
else
(v,w)'yi bırak;
}
if (|T| < n-1)
printf("minimum kapsayan ağaç yok\n");- T: seçilen kenarlar, E: kalan kenarlar, |T|: seçilen kenar sayısı.
- Döngü n − 1 kenar seçilene kadar döner; henüz n − 1 seçilmemiş ama seçilebilecek kenar kalmamışsa da biter → çizge bağlantılı değildir.
- Kruskal’da seçilen kenarlar ayrık öbekler (orman) oluşturabilir; en sonda tek öbek olur.
- Joseph B. Kruskal, 1956; doğruluğunun ispatı vardır. “İspatı okununca daha iyi anlaşılır.”
Kruskal
Kruskal Adım Adım
| Kenar | w | İşlem |
|---|---|---|
| (0,5) | 10 | |
| (2,3) | 12 | |
| (1,6) | 14 | |
| (1,2) | 16 | |
| (3,6) | 18 | |
| (3,4) | 22 | |
| (4,6) | 24 | |
| (4,5) | 25 | |
| (0,1) | 28 |
Kenarlar maliyete göre sıralandı; her düğüm ayrı bir öbek.
| Kenar | w | İşlem |
|---|---|---|
| (0,5) | 10 | Ağaca ekle |
| (2,3) | 12 | |
| (1,6) | 14 | |
| (1,2) | 16 | |
| (3,6) | 18 | |
| (3,4) | 22 | |
| (4,6) | 24 | |
| (4,5) | 25 | |
| (0,1) | 28 |
(0,5) = 10: uçlar farklı öbeklerde → ağaca eklendi. Toplam 10.
| Kenar | w | İşlem |
|---|---|---|
| (0,5) | 10 | Ağaca ekle |
| (2,3) | 12 | Ağaca ekle |
| (1,6) | 14 | |
| (1,2) | 16 | |
| (3,6) | 18 | |
| (3,4) | 22 | |
| (4,6) | 24 | |
| (4,5) | 25 | |
| (0,1) | 28 |
(2,3) = 12: uçlar farklı öbeklerde → ağaca eklendi. Toplam 22.
| Kenar | w | İşlem |
|---|---|---|
| (0,5) | 10 | Ağaca ekle |
| (2,3) | 12 | Ağaca ekle |
| (1,6) | 14 | Ağaca ekle |
| (1,2) | 16 | |
| (3,6) | 18 | |
| (3,4) | 22 | |
| (4,6) | 24 | |
| (4,5) | 25 | |
| (0,1) | 28 |
(1,6) = 14: uçlar farklı öbeklerde → ağaca eklendi. Toplam 36.
| Kenar | w | İşlem |
|---|---|---|
| (0,5) | 10 | Ağaca ekle |
| (2,3) | 12 | Ağaca ekle |
| (1,6) | 14 | Ağaca ekle |
| (1,2) | 16 | Ağaca ekle |
| (3,6) | 18 | |
| (3,4) | 22 | |
| (4,6) | 24 | |
| (4,5) | 25 | |
| (0,1) | 28 |
(1,2) = 16: uçlar farklı öbeklerde → ağaca eklendi. Toplam 52.
| Kenar | w | İşlem |
|---|---|---|
| (0,5) | 10 | Ağaca ekle |
| (2,3) | 12 | Ağaca ekle |
| (1,6) | 14 | Ağaca ekle |
| (1,2) | 16 | Ağaca ekle |
| (3,6) | 18 | Döngü var, ekleme |
| (3,4) | 22 | |
| (4,6) | 24 | |
| (4,5) | 25 | |
| (0,1) | 28 |
(3,6) = 18: uçlar aynı öbekte → döngü oluşur, bırakılır.
| Kenar | w | İşlem |
|---|---|---|
| (0,5) | 10 | Ağaca ekle |
| (2,3) | 12 | Ağaca ekle |
| (1,6) | 14 | Ağaca ekle |
| (1,2) | 16 | Ağaca ekle |
| (3,6) | 18 | Döngü var, ekleme |
| (3,4) | 22 | Ağaca ekle |
| (4,6) | 24 | |
| (4,5) | 25 | |
| (0,1) | 28 |
(3,4) = 22: uçlar farklı öbeklerde → ağaca eklendi. Toplam 74.
| Kenar | w | İşlem |
|---|---|---|
| (0,5) | 10 | Ağaca ekle |
| (2,3) | 12 | Ağaca ekle |
| (1,6) | 14 | Ağaca ekle |
| (1,2) | 16 | Ağaca ekle |
| (3,6) | 18 | Döngü var, ekleme |
| (3,4) | 22 | Ağaca ekle |
| (4,6) | 24 | Döngü var, ekleme |
| (4,5) | 25 | |
| (0,1) | 28 |
(4,6) = 24: uçlar aynı öbekte → döngü oluşur, bırakılır.
| Kenar | w | İşlem |
|---|---|---|
| (0,5) | 10 | Ağaca ekle |
| (2,3) | 12 | Ağaca ekle |
| (1,6) | 14 | Ağaca ekle |
| (1,2) | 16 | Ağaca ekle |
| (3,6) | 18 | Döngü var, ekleme |
| (3,4) | 22 | Ağaca ekle |
| (4,6) | 24 | Döngü var, ekleme |
| (4,5) | 25 | Ağaca ekle |
| (0,1) | 28 |
(4,5) = 25: uçlar farklı öbeklerde → ağaca eklendi. Toplam 99. n − 1 = 6 kenar seçildi: bitti (28’e hiç bakılmadı).
Kruskal
Kruskal Nasıl Kodlanmaz?
- Gözlem: önce 2 bileşen oldu, sonra 3, sonra tekrar 2’ye düştü. “Yakaladık! Seçtiğim kenarları komşuluk listesinde tutarım.”
- Seçilmeyen kenarlar bir dizide. Kötü: her seferinde en azı ara, sil, kalanları kaydır. Daha iyi: kenarları maliyete göre sıralı tut (uç1, uç2, maliyet).
- (0,5) geldi: listelerde aratırım, ne 0 var ne 5 → yeni liste. (3,2), (6,1) de yeni listelere. (1,2): farklı listelerde → döngü yok, iki listeyi birleştir.
- (3,6): ikisi de aynı listede → döngü! Doğru çalışır ama sürekli bir arama maliyeti var.
Kruskal
Kruskal Nasıl Kodlanır? İki Soru, İki Veri Yapısı
Min öncelikli kuyruk (min-heap). Kenarları tek tek eklemek O(e log e); diziyi aşağıdan yukarıya heap kurmayla heap'e çevirmek O(e). Ya da kenarlar bir kez sıralanır (qsort).
Uçlar iki ayrı kümenin öğesiyse döngü olmaz: union-find. kume_bul(3) ile kume_bul(2) aynı kökü mü veriyor?
Kruskal
C: Kümeler (Union-Find) ve Karşılaştırma
typedef struct { int u, v, w; } kenar; int kume_bul(int ata[], int i) /* kok: ata < 0 */ { while (ata[i] >= 0) i = ata[i]; return i; } void birlestir(int ata[], int a, int b) /* a, b kok */ { if (ata[a] <= ata[b]) { /* a daha buyuk kume */ ata[a] += ata[b]; ata[b] = a; } else { ata[b] += ata[a]; ata[a] = b; } } int kars(const void *x, const void *y) { return ((const kenar *)x)->w - ((const kenar *)y)->w; }
ata[i] < 0: i köktür ve−ata[i]küme boyutudur (başta hepsi −1).- Ağırlıklı birleşim: küçük küme büyüğün altına bağlanır → ağaç yüksekliği O(log n),
kume_bulO(log n). - Yol sıkıştırma (path compression) da eklenirse işlem başına amortize ≈ O(α(n)) — pratikte sabit.
kars:qsortiçin maliyet karşılaştırması (küçük tamsayılar; taşma yok).
Kruskal + union-find · Kodu adım adım çalıştır
Kod İzleme: kruskal() ana döngüsü
Her düğüm kendi kümesinin kökü (ata = −1). Kenarlar maliyete göre sıralanır: O(e log e).
i = 0: (0,5) = 10. kume_bul: 0 → kök 0, 5 → kök 5.
0 ≠ 5 → döngü yok: T’ye eklenir, kümeler birleşir (boyutu büyük olan kök kalır). t = 1.
i = 1: (2,3) = 12. kume_bul: 2 → kök 2, 3 → kök 3.
2 ≠ 3 → döngü yok: T’ye eklenir, kümeler birleşir (boyutu büyük olan kök kalır). t = 2.
i = 2: (1,6) = 14. kume_bul: 1 → kök 1, 6 → kök 6.
1 ≠ 6 → döngü yok: T’ye eklenir, kümeler birleşir (boyutu büyük olan kök kalır). t = 3.
i = 3: (1,2) = 16. kume_bul: 1 → kök 1, 2 → kök 2.
1 ≠ 2 → döngü yok: T’ye eklenir, kümeler birleşir (boyutu büyük olan kök kalır). t = 4.
for (i = 0; i < n; i++) ata[i] = -1; qsort(k, e, sizeof(kenar), kars); for (i = 0, t = 0; i < e && t < n - 1; i++) { int a = kume_bul(ata, k[i].u); int b = kume_bul(ata, k[i].v); if (a != b) { /* dongu yok */ T[t++] = k[i]; birlestir(ata, a, b); } /* yoksa birak */ } if (t < n - 1) printf("agac yok\n");
Kruskal + union-find · Kodu adım adım çalıştır
Kod İzleme: kruskal() (devam)
i = 4: (3,6) = 18. kume_bul: 3 → kök 1, 6 → kök 1.
a = b = 1: iki uç aynı kümede → kenar döngü oluşturur, bırakılır.
i = 5: (3,4) = 22. kume_bul: 3 → kök 1, 4 → kök 4.
1 ≠ 4 → döngü yok: T’ye eklenir, kümeler birleşir (boyutu büyük olan kök kalır). t = 5.
i = 6: (4,6) = 24. kume_bul: 4 → kök 1, 6 → kök 1.
a = b = 1: iki uç aynı kümede → kenar döngü oluşturur, bırakılır.
i = 7: (4,5) = 25. kume_bul: 4 → kök 1, 5 → kök 0.
1 ≠ 0 → döngü yok: T’ye eklenir, kümeler birleşir (boyutu büyük olan kök kalır). t = 6.
t = n − 1 = 6 → döngü biter; (0,1) = 28’e hiç bakılmadı. MST maliyeti 10 + 12 + 14 + 16 + 22 + 25 = 99.
for (i = 0; i < n; i++) ata[i] = -1; qsort(k, e, sizeof(kenar), kars); for (i = 0, t = 0; i < e && t < n - 1; i++) { int a = kume_bul(ata, k[i].u); int b = kume_bul(ata, k[i].v); if (a != b) { /* dongu yok */ T[t++] = k[i]; birlestir(ata, a, b); } /* yoksa birak */ } if (t < n - 1) printf("agac yok\n");
Kruskal
Kruskal’ın Karmaşıklığı (union-find ile)
| Adım | Maliyet | Açıklama |
|---|---|---|
| Sıralama / heap kurma | O(e log e) / O(e) | qsort ya da aşağıdan yukarıya heap kurma |
| En az kenarı almak | O(log e) × en çok e kez | min-heap deleteMin (sıralı dizide O(1)) |
| kume_bul (2 kez / kenar) | O(log n) ağırlıklı; ≈ O(α(n)) sıkıştırmalı | en çok 2e çağrı |
| birlestir | O(1) | tam n − 1 kez |
| Toplam | O(e log e) = O(e log n) | e ≤ n² → log e ≤ 2 log n |
Prim
Prim Algoritması
T = {};
TV = {0}; /* ağaç düğümleri: 0'dan başla */
while (|T| < n-1) {
u ∈ TV ve v ∉ TV olan en ucuz (u,v) kenarını seç;
if (böyle kenar yok) break;
TV = TV ∪ {v};
T = T ∪ {(u,v)};
}
if (|T| < n-1)
printf("minimum kapsayan ağaç yok\n");- Jarník (1930), Prim (1957), Dijkstra (1959) → DJP / Prim-Jarník algoritması.
- Kruskal’da ayrık kenarlar seçilebiliyordu; Prim seçilen kenarları tek öbek hâlinde büyütür.
- Seçilebilecek kenarlar: bir ucu TV’de, diğer ucu TV’de olmayan kenarlar.
- Tek bir öbek giderek büyür; döngü denetimi gerekmez (v ∉ TV zaten garanti eder).
Prim
Prim Adım Adım
| TV | T | Seçilebilecek |
|---|---|---|
| {0} | {} | (0,1), (0,5) |
Aday kenarlar sarı. En ucuzu (0,5) = 10 → 5 ağaca katılır.
| TV | T | Seçilebilecek |
|---|---|---|
| {0, 5} | {(0,5)} | (0,1), (5,4) |
Aday kenarlar sarı. En ucuzu (5,4) = 25 → 4 ağaca katılır.
| TV | T | Seçilebilecek |
|---|---|---|
| {0, 5, 4} | {(0,5), (5,4)} | (0,1), (4,3), (4,6) |
Aday kenarlar sarı. En ucuzu (4,3) = 22 → 3 ağaca katılır.
| TV | T | Seçilebilecek |
|---|---|---|
| {0, 5, 4, 3} | {(0,5), (5,4), (4,3)} | (0,1), (3,2), (3,6), (4,6) |
Aday kenarlar sarı. En ucuzu (3,2) = 12 → 2 ağaca katılır.
| TV | T | Seçilebilecek |
|---|---|---|
| {0, 5, 4, 3, 2} | {(0,5), (5,4), (4,3), (3,2)} | (0,1), (2,1), (3,6), (4,6) |
Aday kenarlar sarı. En ucuzu (2,1) = 16 → 1 ağaca katılır.
| TV | T | Seçilebilecek |
|---|---|---|
| {0, 5, 4, 3, 2, 1} | {(0,5), (5,4), (4,3), (3,2), (2,1)} | (1,6), (3,6), (4,6) |
Aday kenarlar sarı. En ucuzu (1,6) = 14 → 6 ağaca katılır.
| TV | T | Seçilebilecek |
|---|---|---|
| {0, 5, 4, 3, 2, 1, 6} | {(0,5), (5,4), (4,3), (3,2), (2,1), (1,6)} | — |
TV tüm düğümleri içeriyor: 6 kenar, toplam 99 — Kruskal ile aynı ağaç (ağırlıklar farklı olduğundan MST tektir).
Son adımlarda (0,1) kenarı “seçilebilecek” gibi görünse de iki ucu da TV’de olduğundan aday değildir.
Prim
Prim’i Kodlamak: Asıl Zorluk
- En az maliyetli aday kenar tek hamlede alınmalı.
- Bir kenar seçilince aday kümesine yeni kenarlar eklenir, bazıları çıkar (iki ucu da TV’ye düşenler): A kümesi B olur.
- 0 seçildiğinde yalnızca (0,1) ve (0,5)’i gören bir veri yapısı olmalı; diğerlerine bakmamalı bile.
- Çözüm (ek): her v ∉ TV için
enaz[v]= TV’ye en ucuz bağlantı. Dizi ile O(n²); min-heap ile O(e log n).
Prim, O(n²) diziler · Kodu adım adım çalıştır
Kod İzleme: prim() (enaz/yakin dizileri)
TV = {0}. enaz[v] = w[0][v]: 0’a bağlı 1 (28) ve 5 (10); diğerleri ∞. yakin[v] = 0.
En küçük enaz: u = 5 (10), kenar (0,5) ağaca girer. toplam = 10.
5 üzerinden daha ucuz bağlantılar: enaz[4] = 25 (yakin = 5).
En küçük enaz: u = 4 (25), kenar (5,4) ağaca girer. toplam = 35.
4 üzerinden daha ucuz bağlantılar: enaz[3] = 22, enaz[6] = 24 (yakin = 4).
En küçük enaz: u = 3 (22), kenar (4,3) ağaca girer. toplam = 57.
3 üzerinden daha ucuz bağlantılar: enaz[2] = 12, enaz[6] = 18 (yakin = 3).
En küçük enaz: u = 2 (12), kenar (3,2) ağaca girer. toplam = 69.
2 üzerinden daha ucuz bağlantılar: enaz[1] = 16 (yakin = 2).
En küçük enaz: u = 1 (16), kenar (2,1) ağaca girer. toplam = 85.
1 üzerinden daha ucuz bağlantılar: enaz[6] = 14 (yakin = 1).
En küçük enaz: u = 6 (14), kenar (1,6) ağaca girer. toplam = 99.
n − 1 düğüm eklendi: toplam 99. Her tur O(n) arama + O(n) güncelleme → O(n²); yoğun çizgede idealdir.
secildi[0] = 1; /* TV = {0} */ for (v = 0; v < n; v++) enaz[v] = w[0][v], yakin[v] = 0; for (k = 1; k < n; k++) { u = en_yakin(enaz, secildi, n); /* O(n) */ if (u < 0) break; /* agac yok */ secildi[u] = 1; toplam += enaz[u]; for (v = 0; v < n; v++) if (!secildi[v] && w[u][v] < enaz[v]) enaz[v] = w[u][v], yakin[v] = u; }
Prim
Prim’i Heap ile Hızlandırmak (ek konu)
/* komsuluk listesi + min-heap (anahtar = enaz) */ heap_ekle(H, 0, 0); while (!heap_bos(H)) { u = heap_min_al(H); /* O(log n) */ if (secildi[u]) continue; /* bayat kayit */ secildi[u] = 1; for (p = baslik[u]; p; p = p->bag) if (!secildi[p->dugum] && p->agirlik < enaz[p->dugum]) { enaz[p->dugum] = p->agirlik; yakin[p->dugum] = u; heap_ekle(H, p->dugum, p->agirlik); } }
| Uygulama | Zaman | Uygun |
|---|---|---|
| Dizi (yukarıdaki) | O(n²) | yoğun çizge (e ≈ n²) |
| İkili heap + liste | O(e log n) | seyrek çizge |
| Fibonacci heap | O(e + n log n) | kuramsal |
| Kruskal | O(e log e) | seyrek, kenar listesi hazır |
Prim
Problem Çözmede Yaklaşım
“To follow the path: look to the master, follow the master, walk with the master, see through the master, become the master.”
Yani önce ustanın (literatürün) çözümü öğrenilir, sonra özgün çözüm üretilir.
Prim
Ödev: Kruskal’ı Veri Yapısıyla Kodlayın
“Haftaya herkes Kruskal Algoritması’nı anlattığım şekilde kodlayıp getiriyor.” Beklenen: min-heap + union-find, girdi kenar listesi, çıktı ağaç kenarları ve toplam maliyet.
- Kenar yapısı
kenar {u, v, w}; heap’i diziden aşağıdan yukarıya kurun (O(e)). kume_bul’a yol sıkıştırma ekleyin;birlestirağırlıklı olsun.- Çizge bağlantılı değilse “minimum kapsayan ağaç yok” yazsın (|T| < n − 1).
- 7 köy örneğinde 99 bulduğunu, (3,6) ve (4,6)’nın reddedildiğini gösteren bir çıktı üretin.
Kontrol listesi
- Heap’ten
ekez değil, t = n − 1 olunca durun. - Her
mallociçin NULL kontrolü, sondafree. - Eşit ağırlıklarda MST tek olmayabilir; toplam maliyet yine aynıdır.
Prim
Diğer MST Algoritmaları: Sollin (Borůvka), Reverse-Delete
Başta her düğüm ayrı bir bileşen.
Tur 1: her bileşen kendisinden çıkan en ucuz kenarı seçer (sarı): (0,5)=10, (1,6)=14, (2,3)=12, (3,4)=22. Kalan bileşen: 3.
Tur 2: her bileşen kendisinden çıkan en ucuz kenarı seçer (sarı): (4,5)=25, (1,2)=16. Kalan bileşen: 1. Toplam 99.
- Sollin / Borůvka (1926): her turda her bileşen en ucuz dış kenarını seçer; bileşen sayısı en az yarıya iner → en çok log n tur, O(e log n). Paralelleştirmeye uygundur.
- Reverse-delete: kenarlar azalan maliyetle denenir; kenarı silmek çizgeyi koparmıyorsa kenar silinir. Kruskal’ın tersi.
- Hepsi aynı kesit/döngü özelliklerine dayanır ve (farklı ağırlıklarda) aynı ağacı bulur.
Prim
MST ile En Kısa Yol Ağacı Aynı Şey Değildir
Toplam kenar maliyetini en aza indirir; kökü yoktur. 0’dan 1’e ağaç üzerinden uzaklık: 85.
Her düğümün 0’a uzaklığını en aza indirir: d(1) = 28, ama toplam kenar maliyeti 105 > 99.
Prim
Kruskal ile Prim’in Karşılaştırması
| Kruskal | Prim | |
|---|---|---|
| Büyüyen yapı | orman (ayrık öbekler birleşir) | tek ağaç (TV) |
| Seçim ölçütü | tüm kenarlar arasında en ucuzu | TV’den çıkan en ucuz kenar |
| Döngü denetimi | union-find gerekir | gerekmez (v ∉ TV) |
| Veri yapısı | sıralı dizi / min-heap + kümeler | enaz[] dizisi / min-heap |
| Zaman | O(e log e) | O(n²) ya da O(e log n) |
| İyi olduğu yer | seyrek çizge, kenar listesi | yoğun çizge, matris |
| Bağlantılı olmayan çizge | minimum kapsayan orman verir | yalnızca 0’ın bileşenini kapsar |
Prim
Sınav Tarzı Soru: Kruskal ve Prim’i İşletin
Kruskal’ın kodlanması ödev olarak verildi; aşağıdaki soru sınavlarda sorulabilecek tarzdadır.
- Kruskal ile MST’yi bulun; her kenar için “ekle / döngü” kararını yazın.
- Prim’i A’dan başlatıp seçilen kenar sırasını yazın.
- Union-find
ata[]dizisinin son hâlini verin (ağırlıklı birleşim).
Çözüm
Kruskal: (C,F)=1 ekle, (B,E)=2 ekle, (A,D)=3 ekle, (A,B)=4 ekle, (B,D)=5 döngü, (B,C)=6 ekle. Toplam 16.
Prim (A): (A,D)=3 → (A,B)=4 → (B,E)=2 → (B,C)=6 → (C,F)=1. Toplam 16 — aynı ağaç.
ata[] (A..F = 0..5, kenarlar (u,v) sırasıyla birlestir(kume_bul(u), kume_bul(v))): [-6, 0, 0, 0, 1, 2]
Özet
Karmaşıklık Özeti
| Algoritma | Yapı | Zaman | Not |
|---|---|---|---|
| Tüm alt kümeleri dene | — | C(e, n−1) × O(n) | üstel, pratik değil |
| Kruskal (kötü kodlama) | listeler + arama | O(e · n) | “böyle kodlamayın” |
| Kruskal | sıralama/min-heap + union-find | O(e log e) | orman büyür, birleşir |
| Prim | diziler (enaz, yakin) | O(n²) | yoğun çizge |
| Prim | ikili heap + komşuluk listesi | O(e log n) | seyrek çizge |
| DFS/BFS kapsayan ağacı | yığın / kuyruk | O(n + e) | ağırlıksız |
Özet
Sık Yapılan Hatalar
kume_bul!).Özet
Alıştırmalar
- 7 köy örneğinde (3,6) kenarının maliyeti 18 yerine 11 olsaydı MST ve maliyeti ne olurdu?
- Kruskal ve Prim aynı çizgede hangi koşulda farklı ağaçlar verebilir?
- 8 düğümlü örnek çizgede 7’den başlayan DFS ve BFS kapsayan ağaçlarının kenarlarını yazın.
- K4’ün kaç kapsayan ağacı vardır? Kaçı yol, kaçı yıldız biçimlidir?
- Kenar ağırlıkları 1..10 arası tamsayıysa Kruskal’ın sıralama adımı nasıl O(e)’ye iner?
- Prim’in O(n²) sürümü neden seyrek çizgede kötüdür?
Cevaplar / ipuçları
- Sıra: (0,5)=10 ekle, (3,6)=11 ekle, (2,3)=12 ekle, (1,6)=14 ekle, (1,2)=16 döngü, (3,4)=22 ekle, (4,6)=24 döngü, (4,5)=25 ekle → MST {(0,5),(3,6),(2,3),(1,6),(3,4),(4,5)}, maliyet 94.
- Yalnızca eşit ağırlıklı kenarlar varsa; toplam maliyet yine aynıdır.
- Sıralı listelerle DFS(7): (7,3),(3,1),(1,0),(0,2),(2,5),(2,6),(1,4); BFS(7): (7,3),(7,4),(7,5),(7,6),(3,1),(5,2),(1,0).
- 16: 12 yol + 4 yıldız.
- Sayma sıralaması (kova) ile O(e + 10).
- Her turda n düğümü tarar: O(n²) ≫ O(e log n) (e ≈ n iken).
Özet
Bölüm Özeti
n − 1 kenarlı, döngüsüz, tüm düğümleri bağlayan alt çizge; DFS/BFS birer tane üretir.
Toplam ağırlığı en küçük kapsayan ağaç; açgözlü seçim doğru sonuç verir (kesit özelliği).
Kenarlar artan maliyetle denenir; döngü union-find ile yakalanır: O(e log e).
Tek öbek en ucuz kenarla büyür: diziyle O(n²), heap ile O(e log n).
Aynı fikir, farklı yapı: liste araması O(e·n) → kümeler O(e log e).
Seyrek çizgede Kruskal ya da heap’li Prim; yoğun çizgede dizili Prim.
Özet
Kaynaklar
- E. Horowitz, S. Sahni, S. Anderson-Freed, Fundamentals of Data Structures in C, 2. baskı, §6.3 Minimum Cost Spanning Trees.
- T. H. Cormen ve ark., Introduction to Algorithms, 4. baskı, Bölüm 19 (Disjoint Sets) ve Bölüm 21 (Minimum Spanning Trees).
- R. Sedgewick, Algorithms in C, Part 5, Bölüm 20 (Minimum Spanning Trees).
İlgili: Çizgeler · Kümeler ve Union-Find