📦 10.2: Graf Temsil Yöntemleri

Adjacency Matrix ve Adjacency List - Karşılaştırmalı Anlatım

📌 Neden Farklı Temsil Yöntemleri?

Grafları bellekte saklamanın birden fazla yolu vardır. Hangi yöntemi seçeceğimiz grafın yoğunluğuna, yapacağımız operasyonlara ve bellek kısıtlamalarına bağlıdır.

📊

Adjacency Matrix

2D dizi ile temsil

📋

Adjacency List

Komşuluk listeleri

🔗

Edge List

Kenar listesi

📊 Adjacency Matrix (Komşuluk Matrisi)

Adjacency Matrix, V×V boyutunda 2 boyutlu bir dizidir. matrix[i][j] = 1 ise i ve j düğümleri arasında kenar vardır, 0 ise yoktur.

📐 Matris Gösterimi

A
B
C
D
A
0
1
0
1
B
1
0
1
0
C
0
1
0
1
D
1
0
1
0

✅ Avantajlar

  • O(1) kenar kontrolü: i-j arası kenar var mı? Anında!
  • Basit implementasyon: 2D dizi kullanımı kolay
  • Yoğun graflar için ideal: Çok kenar varsa verimli

❌ Dezavantajlar

  • O(V²) bellek: Kenar sayısından bağımsız!
  • O(V) komşu bulma: Tüm satırı taramak gerekir
  • Seyrek graflar için israf: Çoğu hücre 0 olur
Adjacency Matrix - Python Implementasyonu
class GraphMatrix:
    """Adjacency Matrix ile Graf Implementasyonu"""
    
    def __init__(self, vertices):
        self.V = vertices
        # V x V boyutunda sıfırlarla dolu matris
        self.matrix = [[0] * vertices for _ in range(vertices)]
        # Düğüm isimleri (opsiyonel)
        self.vertex_names = [chr(65 + i) for i in range(vertices)]  # A, B, C, ...
    
    def add_edge(self, u, v, weight=1):
        """Yönsüz graf için kenar ekle"""
        self.matrix[u][v] = weight
        self.matrix[v][u] = weight  # Yönsüz: iki yönlü
    
    def add_directed_edge(self, u, v, weight=1):
        """Yönlü graf için kenar ekle"""
        self.matrix[u][v] = weight
    
    def has_edge(self, u, v):
        """Kenar var mı kontrol et - O(1)"""
        return self.matrix[u][v] != 0
    
    def get_neighbors(self, u):
        """Komşuları bul - O(V)"""
        neighbors = []
        for v in range(self.V):
            if self.matrix[u][v] != 0:
                neighbors.append(v)
        return neighbors
    
    def get_degree(self, u):
        """Düğümün derecesi - O(V)"""
        return sum(1 for v in range(self.V) if self.matrix[u][v] != 0)
    
    def display(self):
        """Matrisi görsel göster"""
        print("   ", end="")
        for name in self.vertex_names:
            print(f" {name} ", end="")
        print()
        
        for i in range(self.V):
            print(f" {self.vertex_names[i]} ", end="")
            for j in range(self.V):
                print(f" {self.matrix[i][j]} ", end="")
            print()


# Test
print("=== ADJACENCY MATRIX ===\n")

g = GraphMatrix(4)  # A, B, C, D

# Kenarları ekle (yönsüz)
edges = [(0, 1), (0, 3), (1, 2), (2, 3)]  # A-B, A-D, B-C, C-D
for u, v in edges:
    g.add_edge(u, v)

print("Graf matrisi:")
g.display()

print(f"\nA-B arası kenar var mı? {g.has_edge(0, 1)}")
print(f"A-C arası kenar var mı? {g.has_edge(0, 2)}")

print(f"\nB'nin komşuları: {[g.vertex_names[n] for n in g.get_neighbors(1)]}")
print(f"A'nın derecesi: {g.get_degree(0)}")

print("\n📊 Bellek kullanımı: V² = 4² = 16 hücre")
Çıktı bekleniyor...

📋 Adjacency List (Komşuluk Listesi)

Adjacency List, her düğüm için o düğümün komşularının listesini tutar. Python'da dictionary + list kombinasyonu ile kolayca yapılır.

📋 Liste Gösterimi

A
B D
B
A C
C
B D
D
A C

✅ Avantajlar

  • O(V + E) bellek: Sadece var olan kenarlar saklanır
  • O(degree) komşu bulma: Komşular hemen listede
  • Seyrek graflar için ideal: Bellek verimli

❌ Dezavantajlar

  • O(degree) kenar kontrolü: Listeyi taramak gerekebilir
  • Biraz daha karmaşık: Dict + list yönetimi
Adjacency List - Python Implementasyonu
from collections import defaultdict

class GraphList:
    """Adjacency List ile Graf Implementasyonu"""
    
    def __init__(self, directed=False):
        self.graph = defaultdict(list)
        self.directed = directed
    
    def add_edge(self, u, v, weight=None):
        """Kenar ekle"""
        if weight is not None:
            self.graph[u].append((v, weight))
            if not self.directed:
                self.graph[v].append((u, weight))
        else:
            self.graph[u].append(v)
            if not self.directed:
                self.graph[v].append(u)
    
    def has_edge(self, u, v):
        """Kenar var mı kontrol et - O(degree)"""
        neighbors = self.graph[u]
        # Ağırlıklı kenar durumunu da kontrol et
        for n in neighbors:
            if isinstance(n, tuple):
                if n[0] == v:
                    return True
            else:
                if n == v:
                    return True
        return False
    
    def get_neighbors(self, u):
        """Komşuları getir - O(1)"""
        return self.graph[u]
    
    def get_degree(self, u):
        """Düğümün derecesi"""
        return len(self.graph[u])
    
    def get_all_vertices(self):
        """Tüm düğümleri getir"""
        return list(self.graph.keys())
    
    def display(self):
        """Listeyi görsel göster"""
        for vertex in sorted(self.graph.keys()):
            neighbors = self.graph[vertex]
            print(f"  {vertex} → {neighbors}")


# Test
print("=== ADJACENCY LIST ===\n")

g = GraphList(directed=False)

# Kenarları ekle
edges = [('A', 'B'), ('A', 'D'), ('B', 'C'), ('C', 'D')]
for u, v in edges:
    g.add_edge(u, v)

print("Graf listesi:")
g.display()

print(f"\nA-B arası kenar var mı? {g.has_edge('A', 'B')}")
print(f"A-C arası kenar var mı? {g.has_edge('A', 'C')}")

print(f"\nB'nin komşuları: {g.get_neighbors('B')}")
print(f"A'nın derecesi: {g.get_degree('A')}")

print("\n📋 Bellek kullanımı: V + 2E = 4 + 2×4 = 12 hücre")

# Ağırlıklı graf örneği
print("\n=== AĞIRLIKLI GRAF ===\n")

weighted = GraphList(directed=False)
weighted.add_edge('İstanbul', 'Ankara', 450)
weighted.add_edge('İstanbul', 'Bursa', 150)
weighted.add_edge('Ankara', 'İzmir', 580)
weighted.add_edge('Bursa', 'İzmir', 350)

print("Şehirler arası mesafeler (km):")
weighted.display()
Çıktı bekleniyor...

🔗 Edge List (Kenar Listesi)

Edge List, tüm kenarları basit bir liste olarak saklar. Her kenar (u, v) veya (u, v, weight) tuple'ı olarak tutulur. En basit temsil yöntemidir.

Edge List - Python Implementasyonu
class GraphEdgeList:
    """Edge List ile Graf Implementasyonu"""
    
    def __init__(self, directed=False):
        self.edges = []
        self.directed = directed
        self.vertices = set()
    
    def add_edge(self, u, v, weight=None):
        """Kenar ekle"""
        self.vertices.add(u)
        self.vertices.add(v)
        
        if weight is not None:
            self.edges.append((u, v, weight))
        else:
            self.edges.append((u, v))
    
    def has_edge(self, u, v):
        """Kenar var mı - O(E)"""
        for edge in self.edges:
            if edge[0] == u and edge[1] == v:
                return True
            if not self.directed and edge[0] == v and edge[1] == u:
                return True
        return False
    
    def get_neighbors(self, u):
        """Komşuları bul - O(E)"""
        neighbors = []
        for edge in self.edges:
            if edge[0] == u:
                neighbors.append(edge[1])
            elif not self.directed and edge[1] == u:
                neighbors.append(edge[0])
        return neighbors
    
    def display(self):
        print(f"Düğümler: {sorted(self.vertices)}")
        print(f"Kenarlar: {self.edges}")


# Test
print("=== EDGE LIST ===\n")

g = GraphEdgeList(directed=False)
g.add_edge('A', 'B')
g.add_edge('A', 'D')
g.add_edge('B', 'C')
g.add_edge('C', 'D')

g.display()

print(f"\nA'nın komşuları: {g.get_neighbors('A')}")

print("\n💡 Edge List genellikle graf algoritmalarında ara temsil olarak kullanılır")
print("   (örn: Kruskal MST algoritması kenarları ağırlığa göre sıralar)")
Çıktı bekleniyor...

⚖️ Karşılaştırma Tablosu

Operasyon Adjacency Matrix Adjacency List Edge List
Bellek O(V²) O(V + E) O(E)
Kenar Ekle O(1) O(1) O(1)
Kenar Kontrol O(1) ✨ O(degree) O(E)
Komşu Bul O(V) O(degree) ✨ O(E)
Tüm Kenarları Gez O(V²) O(V + E) O(E)

📊 Matrix Kullan:

  • Yoğun graflar (çok kenar)
  • Sık kenar kontrolü gerekiyorsa
  • Küçük graflar (V < 1000)
  • Floyd-Warshall gibi matris algoritmalarında

📋 List Kullan:

  • Seyrek graflar (az kenar)
  • BFS/DFS gibi traversal'larda
  • Büyük graflar (sosyal ağlar)
  • Dijkstra, Prim gibi algoritmalarda

🐍 Pratik: Python Dictionary ile Graf

Gerçek dünyada en sık kullandığımız yöntem: Python dict ile adjacency list.

En Pratik Yöntem: dict ile Graf
# En basit ve pratik graf temsili!
# dict comprehension ile kolayca oluşturulabilir

# 1. Yönsüz, ağırlıksız graf
graph = {
    'A': ['B', 'C'],
    'B': ['A', 'D', 'E'],
    'C': ['A', 'F'],
    'D': ['B'],
    'E': ['B', 'F'],
    'F': ['C', 'E']
}

print("=== YÖNSÜZ GRAF ===")
for node, neighbors in graph.items():
    print(f"  {node}: {neighbors}")

# 2. Yönlü, ağırlıklı graf (şehirler arası mesafe)
weighted_graph = {
    'İstanbul': {'Ankara': 450, 'Bursa': 150},
    'Ankara': {'İzmir': 580, 'Konya': 260},
    'Bursa': {'İzmir': 350},
    'İzmir': {'Antalya': 450},
    'Konya': {'Antalya': 310},
    'Antalya': {}
}

print("\n=== AĞIRLIKLI GRAF (Şehirler arası km) ===")
for city, connections in weighted_graph.items():
    print(f"  {city}: {connections}")

# Pratik fonksiyonlar
def get_neighbors(graph, node):
    """Komşuları getir"""
    return graph.get(node, [])

def has_edge(graph, u, v):
    """Kenar var mı?"""
    return v in graph.get(u, [])

def add_edge(graph, u, v, directed=False):
    """Kenar ekle"""
    if u not in graph:
        graph[u] = []
    if v not in graph:
        graph[v] = []
    
    graph[u].append(v)
    if not directed:
        graph[v].append(u)

print("\n=== FONKSİYON TESTLERİ ===")
print(f"A'nın komşuları: {get_neighbors(graph, 'A')}")
print(f"A-B kenar var mı? {has_edge(graph, 'A', 'B')}")
print(f"A-D kenar var mı? {has_edge(graph, 'A', 'D')}")
Çıktı bekleniyor...

🧮 Bellek Hesaplama Örneği

📱 Facebook Örneği

2 milyar kullanıcı, ortalama 200 arkadaş

Adjacency Matrix: 2×10⁹ × 2×10⁹ = 4×10¹⁸ bit ≈ 500 Petabyte!
Adjacency List: 2×10⁹ × 200 = 4×10¹¹ ≈ 1.5 Terabyte ✅

Fark: ~333,000 kat! Seyrek graflarda adjacency list şart.