Kruskal's Algorithm (Kruskal Algoritması)

Kruskal algoritması, ağırlıklı bağlı bir grafta Minimum Spanning Tree (Minimum Yayılma Ağacı) bulan açgözlü bir algoritmadır. Joseph Kruskal tarafından 1956'da geliştirilmiş olup, kenar tabanlı bir yaklaşım kullanır ve Union-Find veri yapısından yararlanır.

Avantajlar

  • Sparse (seyrek) graflar için verimlidir
  • Kenar tabanlı yaklaşımı anlaşılması kolaydır
  • Union-Find optimizasyonları ile hızlı çalışır
  • Tüm kenarları aynı anda işleyebilir
  • Paralel işleme uygun bir yapıya sahiptir
  • Deterministik sonuçlar verir

Dezavantajlar

  • Kenarları sıralama işlemi O(E log E) zaman alır
  • Dense (yoğun) graflar için Prim algoritmasından yavaş olabilir
  • Union-Find veri yapısı ek karmaşıklık getirir
  • Başlangıç düğümü seçimi yapılamaz
  • Büyük edge sayısına sahip graflarda bellek sorunu yaşanabilir

İnteraktif Görselleştirme

2119131117012345

Kenar Renkleri

MST'de bulunan kenar
Şu anda eklenen kenar
Reddedilen kenar (döngü oluşturur)
İncelenen kenar
Normal kenar

Düğüm Renkleri

Normal düğüm

Kruskal's Algorithm

Zaman Karmaşıklığı: O(E log E)

Alan Karmaşıklığı: O(V)

Yaklaşım: Kenar tabanlı, Union-Find kullanır

Çalışma Prensibi: Tüm kenarları ağırlığa göre sıralar, döngü oluşturmayan en küçük kenarları ekler

Avantaj: Sparse graflarda verimli

Prim's Algorithm

Zaman Karmaşıklığı: O(E log V)

Alan Karmaşıklığı: O(V)

Yaklaşım: Düğüm tabanlı, öncelik kuyruğu kullanır

Çalışma Prensibi: Bir düğümden başlar, her adımda MST'ye en yakın düğümü ekler

Avantaj: Dense graflarda verimli

Kod Örnekleri

Kruskal's Algorithm - TypeScript
1interface Edge {
2 from: string;
3 to: string;
4 weight: number;
5}
6
7interface UnionFind {
8 parent: Map;
9 rank: Map;
10}
11
12// Kruskal's algorithm for Minimum Spanning Tree
13function kruskal(graph: Graph): {
14 mstEdges: Edge[];
15 totalWeight: number;
16 steps: { edge: Edge; action: 'add' | 'reject'; reason: string }[];
17} {
18 const sortedEdges = [...graph.edges].sort((a, b) => a.weight - b.weight);
19 const mstEdges: Edge[] = [];
20 const steps: { edge: Edge; action: 'add' | 'reject'; reason: string }[] = [];
21 const unionFind = createUnionFind(Array.from(graph.nodes.keys()));
22 let totalWeight = 0;
23
24 // Process edges in order of increasing weight
25 for (const edge of sortedEdges) {
26 const rootFrom = find(unionFind, edge.from);
27 const rootTo = find(unionFind, edge.to);
28
29 // Check if adding this edge would create a cycle
30 if (rootFrom !== rootTo) {
31 union(unionFind, edge.from, edge.to);
32 mstEdges.push(edge);
33 totalWeight += edge.weight;
34 steps.push({
35 edge,
36 action: 'add',
37 reason: `Added edge (${edge.from} -> ${edge.to}) with weight ${edge.weight}`
38 });
39
40 // Stop when we have n-1 edges for n nodes
41 if (mstEdges.length === graph.nodes.size - 1) {
42 break;
43 }
44 } else {
45 steps.push({
46 edge,
47 action: 'reject',
48 reason: `Rejected edge (${edge.from} -> ${edge.to}) - would create cycle`
49 });
50 }
51 }
52
53 return { mstEdges, totalWeight, steps };
54}
55
56// Union-Find data structure implementation
57function createUnionFind(nodes: string[]): UnionFind {
58 const parent = new Map();
59 const rank = new Map();
60
61 // Initialize each node as its own parent with rank 0
62 nodes.forEach(node => {
63 parent.set(node, node);
64 rank.set(node, 0);
65 });
66
67 return { parent, rank };
68}
69
70// Find operation with path compression
71function find(unionFind: UnionFind, node: string): string {
72 if (unionFind.parent.get(node) !== node) {
73 // Path compression optimization
74 unionFind.parent.set(node, find(unionFind, unionFind.parent.get(node)!));
75 }
76 return unionFind.parent.get(node)!;
77}
78
79// Union operation by rank
80function union(unionFind: UnionFind, node1: string, node2: string): void {
81 const root1 = find(unionFind, node1);
82 const root2 = find(unionFind, node2);
83
84 if (root1 === root2) return;
85
86 const rank1 = unionFind.rank.get(root1)!;
87 const rank2 = unionFind.rank.get(root2)!;
88
89 // Union by rank optimization
90 if (rank1 < rank2) {
91 unionFind.parent.set(root1, root2);
92 } else if (rank1 > rank2) {
93 unionFind.parent.set(root2, root1);
94 } else {
95 unionFind.parent.set(root2, root1);
96 unionFind.rank.set(root1, rank1 + 1);
97 }
98}
99
100// Usage example
101const graph = createExampleGraph();
102const result = kruskal(graph);
103console.log('MST edges:', result.mstEdges);
104console.log('Total weight:', result.totalWeight);

Algoritma Nasıl Çalışır?

Kruskal algoritması, Minimum Spanning Tree problemini kenar tabanlı açgözlü yaklaşımla çözer. Algoritmanın temel prensibi, her adımda mevcut durumda en iyi görünen seçimi yapmaktır. Bu yaklaşım, MST probleminin optimal alt yapı özelliğine sahip olması nedeniyle global optimum sonucu garanti eder.

1. Kenar Sıralama Aşaması

Algoritma, grafın tüm kenarlarını ağırlıklarına göre artan sırada düzenleyerek başlar. Bu sıralama işlemi, algoritmanın toplam zaman karmaşıklığını belirleyen ana faktördür ve O(E log E) zaman gerektirir. Efficient sorting algoritmaları kullanılarak bu işlem optimize edilebilir.

2. Union-Find Veri Yapısının Başlatılması

Her düğüm başlangıçta kendi kümesinin temsilcisi olarak ayarlanır. Union-Find veri yapısı, iki temel optimizasyon ile geliştirilmiştir: path compression ve union by rank. Bu optimizasyonlar, find ve union işlemlerini neredeyse sabit zamanda (amortized α(n)) gerçekleştirmeyi mümkün kılar.

3. Kenar İşleme Döngüsü

Sıralanmış kenar listesi üzerinde iterasyon yapılır. Her kenar için, o kenarın iki uç düğümünün aynı bağlı bileşende olup olmadığı kontrol edilir. Eğer farklı bileşenlerdeyse, kenar MST'ye eklenir ve iki bileşen birleştirilir. Bu işlem, çevrim oluşmasını önler ve MST'nin temel özelliğini korur.

4. Sonlandırma Koşulu

Algoritma, MST'de (V-1) kenar bulunduğunda sonlanır; burada V graf düğümlerinin sayısıdır. Bu, bağlı bir grafın spanning tree'sinin sahip olabileceği minimum kenar sayısıdır. Erken sonlandırma, algoritmanın verimliliğini artırır ve gereksiz kenar kontrollerini önler.

Union-Find Veri Yapısı

Kruskal algoritmasının verimli çalışması, Union-Find (Disjoint Set) veri yapısının etkili implementasyonuna bağlıdır. Bu veri yapısı, dinamik bağlantılılık sorgularını destekler ve iki temel işlem sunar.

Find İşlemi

Find işlemi, bir elemanın hangi kümeye ait olduğunu belirler. Path compression optimizasyonu ile, find işlemi sırasında geçilen tüm düğümler doğrudan kök düğüme bağlanır. Bu optimizasyon, gelecekteki find işlemlerini hızlandırır ve veri yapısının genel performansını önemli ölçüde artırır.

Union İşlemi

Union işlemi, iki farklı kümeyi birleştirir. Union by rank optimizasyonu ile, daha düşük rank'e sahip ağaç, daha yüksek rank'e sahip ağacın altına bağlanır. Bu yaklaşım, ağaç yapısının dengeli kalmasını sağlar ve find işlemlerinin worst-case zaman karmaşıklığını minimize eder.

Prim Algoritması ile Karşılaştırma

KriterKruskalPrim
YaklaşımKenar tabanlıDüğüm tabanlı
Zaman KarmaşıklığıO(E log E)O(E log V)
Veri YapısıUnion-FindPriority Queue
Sparse GraflardaDaha VerimliDaha Az Verimli
Dense GraflardaDaha Az VerimliDaha Verimli
Paralel İşlemeDaha UygunDaha Zor

Performans Analizi

Zaman Karmaşıklığı Detayı

Sıralama: O(E log E) - Dominant factor

Union-Find işlemleri: O(E α(V)) - Neredeyse lineer

Toplam: O(E log E + E α(V)) = O(E log E)

α(V) inverse Ackermann function'dur ve pratikte 5'ten küçük bir sabittir, bu nedenle Union-Find işlemleri neredeyse sabit zaman alır.

Alan Karmaşıklığı Detayı

Union-Find yapısı: O(V) - Parent ve rank dizileri

MST kenar listesi: O(V) - En fazla V-1 kenar

Geçici depolama: O(1) - Sabit alan

Algoritma in-place çalışır ve giriş grafından bağımsız olarak linear alan kullanır. Bu özellik, büyük graflarda bellek verimliliği sağlar.

Pratik Uygulama Örnekleri

Ağ Altyapısı Tasarımı

Telekomünikasyon şirketleri, şehirler arası fiber optik kablo ağlarını planlarken Kruskal algoritmasını kullanır. Her şehir bir düğüm, şehirler arası mesafeler ise kenar ağırlıkları olarak modellenir. Bu yaklaşım, minimum maliyetle tüm şehirleri birbirine bağlayan en ekonomik kablo döşeme planını oluşturur.

Kümeleme Algoritmaları

Machine learning alanında, Kruskal algoritması hierarchical clustering için kullanılır. Veri noktaları arasındaki mesafeler kenar ağırlıkları olarak hesaplanır ve MST oluşturulduktan sonra, en ağır kenarlar kaldırılarak veri kümelere ayrılır. Bu yöntem, özellikle doğal veri gruplarını keşfetmede etkilidir.

Elektronik Devre Tasarımı

PCB (Printed Circuit Board) tasarımında, elektronik bileşenler arasındaki minimum bağlantı maliyetini hesaplamak için Kruskal algoritması uygulanır. Her bileşen bir düğüm, bağlantı maliyetleri ise kenar ağırlıkları olarak tanımlanır. Bu optimizasyon, üretim maliyetlerini minimize eder ve devre performansını artırır.

Görüntü İşleme

Image segmentation uygulamalarında, piksel gruplarını benzerliklerine göre ayırmak için Kruskal algoritması kullanılır. Her piksel bir düğüm, pikseller arası renk veya texture farkları ise kenar ağırlıkları olarak hesaplanır. MST oluşturulduktan sonra, yüksek ağırlıklı kenarlar kaldırılarak görüntü anlamlı bölgelere ayrılır.

💡 Optimizasyon İpuçları

Kenar Sıralama Optimizasyonu: Çok büyük graflarda, external sorting veya radix sort gibi specialized sorting algoritmaları kullanarak sıralama performansını artırabilirsiniz.

Parallel Processing: Kenar sıralama işlemi parallelizable olduğu için, multi-core sistemlerde parallel sorting algoritmalarından yararlanarak önemli performans kazanımları elde edebilirsiniz.

Memory Management: Çok büyük graflarda, kenar listesini memory'de tutmak yerine, streaming approach kullanarak disk tabanlı processing yapabilirsiniz.

Early Termination: MST'de (V-1) kenar bulunduğunda algoritmanın erken sonlandırılması, özellikle dense graflarda önemli zaman tasarrufu sağlar.