Gerçek Dünya Problemlerini Graflarla Çözmek
Graflar, günlük hayatımızda farkında olmadan sürekli kullandığımız sistemlerin temelini oluşturur. Google Maps'te yol tarifi aldığınızda, Facebook'ta arkadaş önerisi gördüğünüzde, Netflix'te "Bunları da beğenebilirsiniz" önerisi aldığınızda arka planda graf algoritmaları çalışmaktadır.
Google Maps, Waze, Yandex: Şehirler düğüm, yollar kenar. Dijkstra ve A* algoritmaları ile en kısa/hızlı rota bulunur. Trafik bilgisi kenar ağırlıklarını dinamik günceller.
Facebook, LinkedIn, Twitter: Kişiler düğüm, arkadaşlıklar kenar. "Ortak arkadaş" önerileri BFS ile, topluluk tespiti clustering algoritmaları ile yapılır.
Google PageRank: Web sayfaları düğüm, linkler yönlü kenar. Bir sayfaya gelen link sayısı ve kalitesi "önem" skorunu belirler.
Amazon, UPS, FedEx: Depolar ve müşteriler düğüm, teslimat rotaları kenar. TSP ve VRP (Vehicle Routing Problem) algoritmaları kullanılır.
DNA Sequencing: k-mer parçaları düğüm, örtüşmeler kenar. Euler yolu ile genom assembly yapılır. Protein etkileşim ağları hastalık araştırmalarında kritik.
PERT/CPM: Görevler düğüm, bağımlılıklar kenar. Topological sort ile görev sırası, kritik yol analizi ile proje süresi hesaplanır.
NPC Pathfinding: Harita grid düğüm, hareket yönleri kenar. A* algoritması ile düşmanlar/karakterler hedefe en kısa yoldan gider.
Şebeke Tasarımı: MST algoritmaları ile minimum kablo kullanarak tüm bölgeleri birbirine bağlama. Fiber optik ağ planlaması.
Knowledge Graphs: Google, Amazon, Netflix öneri sistemleri. Kavramlar düğüm, ilişkiler kenar. GNN (Graph Neural Networks) ile öğrenme.
Ağ Analizi: Bilgisayarlar düğüm, bağlantılar kenar. Anormal trafik kalıpları, botnet tespiti, saldırı yayılım analizi.
Uçuş Ağları: Havalimanları düğüm, uçuş rotaları kenar. Hub optimizasyonu, gecikme yayılım analizi, kapasite planlaması.
5G/Internet: Router'lar düğüm, bağlantılar kenar. Paket yönlendirme, yük dengeleme, network topology optimization.
Her graf probleminde şu adımları izleyin:
Arkadaş önerisi ve topluluk tespiti
Bir sosyal medya platformundasınız. Kullanıcılara yeni arkadaş önerileri yapmak ve toplulukları (grupları) tespit etmek istiyorsunuz.
from collections import defaultdict
class SocialNetwork:
"""Sosyal ağ graf modeli"""
def __init__(self):
self.friendships = defaultdict(set)
def add_friendship(self, person1, person2):
"""Çift yönlü arkadaşlık ekle"""
self.friendships[person1].add(person2)
self.friendships[person2].add(person1)
def get_friends(self, person):
"""Direkt arkadaşları getir"""
return self.friendships[person]
def suggest_friends(self, person, limit=3):
"""
Arkadaş önerisi - 'Ortak arkadaş' yöntemi
Mantık: Seni tanımayan ama seninle ortak arkadaşları olan kişiler
iyi arkadaş adaylarıdır!
"""
direct_friends = self.get_friends(person)
suggestions = defaultdict(int) # {önerilen: ortak arkadaş sayısı}
# Her arkadaşın arkadaşlarına bak
for friend in direct_friends:
for friend_of_friend in self.get_friends(friend):
# Kendisi veya zaten arkadaş değilse
if friend_of_friend != person and friend_of_friend not in direct_friends:
suggestions[friend_of_friend] += 1
# Ortak arkadaş sayısına göre sırala
sorted_suggestions = sorted(
suggestions.items(),
key=lambda x: x[1],
reverse=True
)
return sorted_suggestions[:limit]
def mutual_friends(self, person1, person2):
"""İki kişinin ortak arkadaşları"""
friends1 = self.get_friends(person1)
friends2 = self.get_friends(person2)
return friends1.intersection(friends2)
# Sosyal ağ oluştur
network = SocialNetwork()
# Arkadaşlıklar
friendships = [
('Ali', 'Veli'), ('Ali', 'Ayşe'), ('Ali', 'Fatma'),
('Veli', 'Mehmet'), ('Veli', 'Ayşe'),
('Ayşe', 'Zeynep'), ('Ayşe', 'Can'),
('Fatma', 'Zeynep'), ('Fatma', 'Deniz'),
('Mehmet', 'Can'), ('Zeynep', 'Can')
]
for p1, p2 in friendships:
network.add_friendship(p1, p2)
print("=== SOSYAL AĞ ANALİZİ ===\n")
print("Arkadaşlık ağı:")
for person in sorted(network.friendships.keys()):
friends = sorted(network.friendships[person])
print(f" {person}: {friends}")
print("\n=== ALİ İÇİN ARKADAŞ ÖNERİLERİ ===")
suggestions = network.suggest_friends('Ali')
for suggested, mutual_count in suggestions:
print(f" {suggested} ({mutual_count} ortak arkadaş)")
print("\n=== ORTAK ARKADAŞLAR ===")
print(f"Ali & Mehmet: {network.mutual_friends('Ali', 'Mehmet')}")
print(f"Ali & Can: {network.mutual_friends('Ali', 'Can')}")
from collections import deque, defaultdict
def find_communities(friendships_list):
"""
Bağlantılı bileşenler = Topluluklar
Her bağlantılı grup ayrı bir topluluk olarak kabul edilir.
"""
# Graf oluştur
graph = defaultdict(set)
for p1, p2 in friendships_list:
graph[p1].add(p2)
graph[p2].add(p1)
visited = set()
communities = []
for person in graph:
if person not in visited:
# Yeni topluluk bulduk - BFS ile keşfet
community = []
queue = deque([person])
while queue:
current = queue.popleft()
if current in visited:
continue
visited.add(current)
community.append(current)
for friend in graph[current]:
if friend not in visited:
queue.append(friend)
communities.append(sorted(community))
return communities
def find_influencers(friendships_list, top_n=3):
"""
Etki sahibi kişileri bul - Derece (arkadaş sayısı) analizi
Daha gelişmiş: PageRank, Betweenness Centrality
"""
degree = defaultdict(int)
for p1, p2 in friendships_list:
degree[p1] += 1
degree[p2] += 1
sorted_by_degree = sorted(
degree.items(),
key=lambda x: x[1],
reverse=True
)
return sorted_by_degree[:top_n]
# Test verileri - iki ayrı topluluk
friendships = [
# Topluluk 1: Üniversite arkadaşları
('Ali', 'Veli'), ('Ali', 'Ayşe'), ('Veli', 'Ayşe'),
('Ayşe', 'Fatma'), ('Fatma', 'Veli'),
# Topluluk 2: İş arkadaşları
('Can', 'Deniz'), ('Can', 'Elif'),
('Deniz', 'Elif'), ('Elif', 'Gül'),
# Topluluk 3: Aile
('Hasan', 'Hüseyin')
]
print("=== TOPLULUK TESPİTİ ===\n")
communities = find_communities(friendships)
for i, community in enumerate(communities, 1):
print(f"Topluluk {i}: {community}")
print(f"\n📊 Toplam {len(communities)} topluluk bulundu")
print("\n=== EN ETKİLİ KİŞİLER (Derece Analizi) ===")
influencers = find_influencers(friendships)
for person, degree in influencers:
print(f" {person}: {degree} bağlantı")
En kısa yol ve alternatif rotalar
Bir şehir içi navigasyon sistemi geliştiriyorsunuz. Kullanıcıya en kısa yolu ve alternatif rotaları göstermeniz gerekiyor.
import heapq
from collections import defaultdict
class CityNavigation:
"""Şehir navigasyon sistemi"""
def __init__(self):
# {location: [(neighbor, distance, road_name), ...]}
self.roads = defaultdict(list)
def add_road(self, loc1, loc2, distance, road_name=""):
"""Çift yönlü yol ekle"""
self.roads[loc1].append((loc2, distance, road_name))
self.roads[loc2].append((loc1, distance, road_name))
def find_shortest_path(self, start, end):
"""Dijkstra ile en kısa yol"""
if start not in self.roads or end not in self.roads:
return None, float('inf'), []
distances = {loc: float('inf') for loc in self.roads}
distances[start] = 0
previous = {loc: (None, "") for loc in self.roads}
pq = [(0, start)]
visited = set()
while pq:
dist, current = heapq.heappop(pq)
if current in visited:
continue
visited.add(current)
if current == end:
break
for neighbor, road_dist, road_name in self.roads[current]:
if neighbor not in visited:
new_dist = dist + road_dist
if new_dist < distances[neighbor]:
distances[neighbor] = new_dist
previous[neighbor] = (current, road_name)
heapq.heappush(pq, (new_dist, neighbor))
# Yolu oluştur
path = []
roads_taken = []
current = end
while current is not None:
path.append(current)
prev_loc, road_name = previous[current]
if road_name:
roads_taken.append(road_name)
current = prev_loc
path.reverse()
roads_taken.reverse()
return path, distances[end], roads_taken
def print_directions(self, start, end):
"""Yol tarifi yazdır"""
path, distance, roads = self.find_shortest_path(start, end)
if distance == float('inf'):
print(f"❌ {start} → {end} arası yol bulunamadı!")
return
print(f"\n🗺️ YOL TARİFİ: {start} → {end}")
print(f"📏 Toplam mesafe: {distance} km\n")
for i in range(len(path) - 1):
from_loc = path[i]
to_loc = path[i + 1]
road = roads[i] if i < len(roads) else ""
print(f" {i+1}. {from_loc} → {to_loc} ({road})")
print(f"\n✅ Varış: {end}")
# İstanbul haritası (basitleştirilmiş)
nav = CityNavigation()
# Ana yollar
nav.add_road('Kadıköy', 'Üsküdar', 8, 'Sahil Yolu')
nav.add_road('Üsküdar', 'Beşiktaş', 5, 'Boğaz Köprüsü')
nav.add_road('Beşiktaş', 'Taksim', 3, 'İnönü Caddesi')
nav.add_road('Taksim', 'Şişli', 4, 'Halaskargazi Caddesi')
nav.add_road('Kadıköy', 'Maltepe', 10, 'E5')
nav.add_road('Maltepe', 'Kartal', 6, 'E5')
nav.add_road('Beşiktaş', 'Şişli', 5, 'Barbaros Bulvarı')
nav.add_road('Şişli', 'Mecidiyeköy', 2, 'Büyükdere Caddesi')
nav.add_road('Taksim', 'Beyoğlu', 1, 'İstiklal Caddesi')
nav.add_road('Üsküdar', 'Kadıköy', 8, 'Sahil Yolu')
print("=== İSTANBUL NAVİGASYON ===")
nav.print_directions('Kadıköy', 'Taksim')
nav.print_directions('Maltepe', 'Mecidiyeköy')
Topological sort ile bağımlılık çözme
Bir yazılım projesinde görevler arasında bağımlılıklar var. Görevleri hangi sırayla yapmanız gerektiğini belirleyin.
from collections import defaultdict, deque
class ProjectScheduler:
"""Görev planlama sistemi - DAG tabanlı"""
def __init__(self):
self.tasks = {} # {task: {'duration': int, 'deps': []}}
self.graph = defaultdict(list) # Bağımlılık grafı
def add_task(self, name, duration, dependencies=None):
"""Görev ekle"""
deps = dependencies or []
self.tasks[name] = {'duration': duration, 'deps': deps}
# Graf kenarları: bağımlılık → görev
for dep in deps:
self.graph[dep].append(name)
# Bağımlılığı olmayan görevleri de grafta tut
if name not in self.graph:
self.graph[name] = []
def topological_sort(self):
"""Kahn algoritması ile topological sort"""
# In-degree hesapla
in_degree = defaultdict(int)
for task in self.tasks:
in_degree[task] = len(self.tasks[task]['deps'])
# In-degree 0 olan görevlerle başla
queue = deque([t for t in self.tasks if in_degree[t] == 0])
result = []
while queue:
task = queue.popleft()
result.append(task)
# Bu göreve bağlı olanların in-degree'sini azalt
for dependent in self.graph[task]:
in_degree[dependent] -= 1
if in_degree[dependent] == 0:
queue.append(dependent)
# Döngü kontrolü
if len(result) != len(self.tasks):
return None # Döngü var!
return result
def has_cycle(self):
"""Döngü var mı?"""
return self.topological_sort() is None
def calculate_critical_path(self):
"""Kritik yol ve minimum proje süresi"""
order = self.topological_sort()
if order is None:
return None, None
# En erken başlama zamanları
earliest_start = {task: 0 for task in self.tasks}
for task in order:
for dep in self.tasks[task]['deps']:
dep_end = earliest_start[dep] + self.tasks[dep]['duration']
earliest_start[task] = max(earliest_start[task], dep_end)
# En son biten görev = proje süresi
project_duration = max(
earliest_start[t] + self.tasks[t]['duration']
for t in self.tasks
)
return order, project_duration
def print_schedule(self):
"""Zamanlama çizelgesi"""
order, duration = self.calculate_critical_path()
if order is None:
print("❌ Döngüsel bağımlılık! Planlama yapılamaz.")
return
print("=== PROJE PLANI ===\n")
print("Görev Sırası:")
# En erken başlama zamanları
earliest_start = {task: 0 for task in self.tasks}
for task in order:
for dep in self.tasks[task]['deps']:
dep_end = earliest_start[dep] + self.tasks[dep]['duration']
earliest_start[task] = max(earliest_start[task], dep_end)
for i, task in enumerate(order, 1):
info = self.tasks[task]
deps_str = ', '.join(info['deps']) if info['deps'] else '-'
start = earliest_start[task]
end = start + info['duration']
print(f" {i}. {task}")
print(f" Süre: {info['duration']} gün | Başlangıç: Gün {start} | Bitiş: Gün {end}")
print(f" Bağımlılıklar: {deps_str}")
print()
print(f"📅 Toplam proje süresi: {duration} gün")
# Yazılım projesi örneği
project = ProjectScheduler()
# Görevler ve bağımlılıkları
project.add_task('Gereksinim Analizi', 5)
project.add_task('Veritabanı Tasarımı', 3, ['Gereksinim Analizi'])
project.add_task('API Tasarımı', 4, ['Gereksinim Analizi'])
project.add_task('Frontend Tasarımı', 3, ['Gereksinim Analizi'])
project.add_task('Backend Geliştirme', 10, ['Veritabanı Tasarımı', 'API Tasarımı'])
project.add_task('Frontend Geliştirme', 8, ['Frontend Tasarımı', 'API Tasarımı'])
project.add_task('Entegrasyon', 4, ['Backend Geliştirme', 'Frontend Geliştirme'])
project.add_task('Test', 5, ['Entegrasyon'])
project.add_task('Deploy', 2, ['Test'])
project.print_schedule()
BFS ile web sayfalarını tarama
Bir arama motoru geliştiriyorsunuz. Web sayfalarını BFS ile tarayarak sayfa bağlantılarını keşfetmeniz gerekiyor.
from collections import deque, defaultdict
class WebCrawler:
"""
BFS tabanlı Web Crawler simülasyonu
Gerçek dünyada: requests + BeautifulSoup kullanılır
"""
def __init__(self):
# Simüle edilmiş web sayfaları
self.web = {
'ana-sayfa': ['hakkimizda', 'urunler', 'blog'],
'hakkimizda': ['ana-sayfa', 'iletisim', 'ekip'],
'urunler': ['ana-sayfa', 'urun-1', 'urun-2', 'urun-3'],
'blog': ['ana-sayfa', 'yazi-1', 'yazi-2'],
'iletisim': ['ana-sayfa', 'hakkimizda'],
'ekip': ['hakkimizda', 'iletisim'],
'urun-1': ['urunler', 'urun-2'],
'urun-2': ['urunler', 'urun-1', 'urun-3'],
'urun-3': ['urunler'],
'yazi-1': ['blog', 'yazi-2'],
'yazi-2': ['blog', 'yazi-1']
}
def crawl_bfs(self, start_url, max_pages=10):
"""BFS ile sayfaları tara"""
visited = set()
queue = deque([(start_url, 0)]) # (url, derinlik)
crawled = []
while queue and len(crawled) < max_pages:
url, depth = queue.popleft()
if url in visited:
continue
# "Sayfayı indir" (simülasyon)
if url in self.web:
visited.add(url)
crawled.append({'url': url, 'depth': depth})
# Linkleri keşfet
for link in self.web[url]:
if link not in visited:
queue.append((link, depth + 1))
return crawled
def build_link_graph(self):
"""Link grafı oluştur"""
return dict(self.web)
def calculate_pagerank(self, iterations=10, damping=0.85):
"""
Basitleştirilmiş PageRank algoritması
Her sayfanın "önemini" hesaplar.
"""
pages = list(self.web.keys())
n = len(pages)
# Başlangıç: eşit değerler
pagerank = {page: 1.0 / n for page in pages}
for _ in range(iterations):
new_pr = {}
for page in pages:
# Bu sayfaya link veren sayfalar
incoming = [p for p in pages if page in self.web.get(p, [])]
rank_sum = 0
for inc_page in incoming:
# Gelen sayfanın rankını, link sayısına böl
outgoing_links = len(self.web.get(inc_page, []))
if outgoing_links > 0:
rank_sum += pagerank[inc_page] / outgoing_links
new_pr[page] = (1 - damping) / n + damping * rank_sum
pagerank = new_pr
return pagerank
# Test
crawler = WebCrawler()
print("=== WEB CRAWLER ===\n")
print("Başlangıç: ana-sayfa")
print("Max sayfa: 10\n")
results = crawler.crawl_bfs('ana-sayfa', max_pages=10)
print("Taranan sayfalar:")
for page in results:
indent = " " * page['depth']
print(f"{indent}📄 {page['url']} (derinlik: {page['depth']})")
print(f"\n✅ Toplam {len(results)} sayfa tarandı")
print("\n=== PAGERANK ANALİZİ ===\n")
pagerank = crawler.calculate_pagerank()
# Sırala ve yazdır
sorted_pr = sorted(pagerank.items(), key=lambda x: x[1], reverse=True)
for page, rank in sorted_pr[:5]:
bar = "█" * int(rank * 100)
print(f" {page:15} {rank:.4f} {bar}")
BFS ve DFS ile yol bulma
from collections import deque
def solve_maze(maze, start, end):
"""
BFS ile labirent çöz
maze: 2D grid (0=yol, 1=duvar)
start: (row, col) başlangıç
end: (row, col) hedef
Returns: En kısa yol veya None
"""
rows, cols = len(maze), len(maze[0])
# 4 yön: yukarı, aşağı, sol, sağ
directions = [(-1, 0), (1, 0), (0, -1), (0, 1)]
visited = set()
queue = deque([(start, [start])]) # ((row, col), path)
while queue:
(row, col), path = queue.popleft()
# Hedefe ulaştık mı?
if (row, col) == end:
return path
if (row, col) in visited:
continue
visited.add((row, col))
# 4 yöne bak
for dr, dc in directions:
new_row, new_col = row + dr, col + dc
# Sınırlar içinde mi?
if 0 <= new_row < rows and 0 <= new_col < cols:
# Duvar değil ve ziyaret edilmemiş mi?
if maze[new_row][new_col] == 0 and (new_row, new_col) not in visited:
queue.append(((new_row, new_col), path + [(new_row, new_col)]))
return None # Yol bulunamadı
def print_maze_solution(maze, path):
"""Labirenti ve çözümü yazdır"""
rows, cols = len(maze), len(maze[0])
path_set = set(path) if path else set()
# Semboller: █=duvar, ·=yol, ★=çözüm, S=başlangıç, E=bitiş
for r in range(rows):
row_str = ""
for c in range(cols):
if (r, c) == path[0] if path else False:
row_str += "🟢" # Başlangıç
elif (r, c) == path[-1] if path else False:
row_str += "🔴" # Bitiş
elif (r, c) in path_set:
row_str += "🟨" # Çözüm yolu
elif maze[r][c] == 1:
row_str += "⬛" # Duvar
else:
row_str += "⬜" # Boş yol
print(row_str)
# Örnek labirent
maze = [
[0, 0, 1, 0, 0, 0],
[1, 0, 1, 0, 1, 0],
[0, 0, 0, 0, 1, 0],
[0, 1, 1, 1, 1, 0],
[0, 0, 0, 0, 0, 0],
[1, 1, 1, 1, 1, 0]
]
start = (0, 0)
end = (5, 5)
print("=== LABİRENT ÇÖZME ===\n")
print("Orijinal labirent (⬛=duvar, ⬜=yol):")
for row in maze:
print("".join("⬛" if cell == 1 else "⬜" for cell in row))
print("\nBaşlangıç:", start)
print("Hedef:", end)
path = solve_maze(maze, start, end)
if path:
print(f"\n✅ Çözüm bulundu! ({len(path)} adım)")
print("\nÇözüm (🟢=başlangıç, 🔴=bitiş, 🟨=yol):")
print_maze_solution(maze, path)
print("\nYol:", " → ".join(str(p) for p in path))
else:
print("\n❌ Çözüm bulunamadı!")
Minimum Spanning Tree (MST), bir grafın tüm düğümlerini birbirine bağlayan, toplam kenar ağırlığı en düşük olan ağaç yapısıdır.
Şehirleri minimum kablo ile bağlama
Bilgisayar ağlarını minimum maliyetle kurma
Köyleri birbirine bağlayan en ucuz yol ağı
Fiber optik kablo döşeme optimizasyonu
Kruskal ve Prim algoritmalarının adım adım çalışmasını izleyin:
class UnionFind:
"""Disjoint Set Union (DSU) / Union-Find veri yapısı"""
def __init__(self, n):
self.parent = list(range(n))
self.rank = [0] * n
def find(self, x):
"""Kök düğümü bul (path compression ile)"""
if self.parent[x] != x:
self.parent[x] = self.find(self.parent[x])
return self.parent[x]
def union(self, x, y):
"""İki kümeyi birleştir (union by rank)"""
px, py = self.find(x), self.find(y)
if px == py:
return False # Zaten aynı kümedeler
# Rank'e göre birleştir
if self.rank[px] < self.rank[py]:
px, py = py, px
self.parent[py] = px
if self.rank[px] == self.rank[py]:
self.rank[px] += 1
return True
def kruskal_mst(n, edges):
"""
Kruskal algoritması ile MST bul
Args:
n: Düğüm sayısı
edges: [(weight, u, v), ...] kenar listesi
Returns:
MST kenarları ve toplam ağırlık
"""
# Kenarları ağırlığa göre sırala
edges.sort()
uf = UnionFind(n)
mst = []
total_weight = 0
for weight, u, v in edges:
# Döngü oluşturmuyorsa ekle
if uf.union(u, v):
mst.append((u, v, weight))
total_weight += weight
print(f" ✅ Kenar ({u}-{v}, ağırlık={weight}) eklendi")
# N-1 kenar bulunca dur
if len(mst) == n - 1:
break
else:
print(f" ❌ Kenar ({u}-{v}) döngü oluşturur, atlandı")
return mst, total_weight
# Örnek: Şehirler arası kablo döşeme
print("=== KRUSKAL ALGORİTMASI ===")
print("Şehirler: 0=Ankara, 1=İstanbul, 2=İzmir, 3=Bursa, 4=Antalya\n")
# (ağırlık, kaynak, hedef)
edges = [
(4, 0, 1), # Ankara-İstanbul
(8, 0, 2), # Ankara-İzmir
(5, 0, 3), # Ankara-Bursa
(10, 0, 4), # Ankara-Antalya
(2, 1, 3), # İstanbul-Bursa
(6, 2, 3), # İzmir-Bursa
(3, 2, 4), # İzmir-Antalya
(7, 3, 4), # Bursa-Antalya
]
print("Kenarlar işleniyor (ağırlığa göre sıralı):\n")
mst, total = kruskal_mst(5, edges)
print(f"\n🌲 MST Kenarları: {mst}")
print(f"📏 Toplam Maliyet: {total} birim")
import heapq
from collections import defaultdict
def prim_mst(n, edges, start=0):
"""
Prim algoritması ile MST bul
Dijkstra'ya çok benzer! Fark:
- Dijkstra: Başlangıçtan toplam mesafe
- Prim: Sadece o kenara mesafe
Args:
n: Düğüm sayısı
edges: [(u, v, weight), ...] kenar listesi
start: Başlangıç düğümü
Returns:
MST kenarları ve toplam ağırlık
"""
# Adjacency list oluştur
graph = defaultdict(list)
for u, v, w in edges:
graph[u].append((v, w))
graph[v].append((u, w))
visited = set()
mst = []
total_weight = 0
# Min heap: (weight, from_node, to_node)
# Başlangıç düğümü için dummy kenar
pq = [(0, -1, start)]
while pq and len(visited) < n:
weight, from_node, to_node = heapq.heappop(pq)
if to_node in visited:
continue
visited.add(to_node)
if from_node != -1:
mst.append((from_node, to_node, weight))
total_weight += weight
print(f" ✅ Düğüm {to_node} eklendi (kenar: {from_node}-{to_node}, ağırlık={weight})")
else:
print(f" 🚀 Başlangıç düğümü: {to_node}")
# Komşuları heap'e ekle
for neighbor, edge_weight in graph[to_node]:
if neighbor not in visited:
heapq.heappush(pq, (edge_weight, to_node, neighbor))
return mst, total_weight
# Aynı örnek
print("=== PRİM ALGORİTMASI ===")
print("Şehirler: 0=Ankara, 1=İstanbul, 2=İzmir, 3=Bursa, 4=Antalya\n")
edges = [
(0, 1, 4), # Ankara-İstanbul
(0, 2, 8), # Ankara-İzmir
(0, 3, 5), # Ankara-Bursa
(0, 4, 10), # Ankara-Antalya
(1, 3, 2), # İstanbul-Bursa
(2, 3, 6), # İzmir-Bursa
(2, 4, 3), # İzmir-Antalya
(3, 4, 7), # Bursa-Antalya
]
print("MST oluşturuluyor (Ankara'dan başlayarak):\n")
mst, total = prim_mst(5, edges, start=0)
print(f"\n🌲 MST Kenarları: {mst}")
print(f"📏 Toplam Maliyet: {total} birim")
| Kruskal | Prim | |
|---|---|---|
| Yaklaşım | Kenar odaklı (global) | Düğüm odaklı (local) |
| Karmaşıklık | O(E log E) | O(E log V) |
| Seyrek graf | ✅ Daha iyi | 🔶 Orta |
| Yoğun graf | 🔶 Orta | ✅ Daha iyi |
| Veri yapısı | Union-Find | Priority Queue |
Her kenardan tam bir kez geçen yol/devre
📚 Tarihçe: Königsberg'in 7 Köprüsü (1736)
Her düğümden tam bir kez geçen yol/devre
⚠️ NP-Complete problem!
| Koşul | Euler Yolu | Euler Devresi |
|---|---|---|
| Graf bağlı mı? | ✅ Evet | ✅ Evet |
| Tek dereceli düğüm sayısı | 0 veya 2 | 0 (tüm düğümler çift dereceli) |
Euler yolu: Her sokaktan bir kez geç (çöp kamyonu rotası)
Euler yolu: Tüm caddelere uğra, minimum tekrar
Euler yolu: k-mer parçalarını birleştirme
Hamilton devresi: Tüm şehirleri en kısa yoldan ziyaret et
from collections import defaultdict, deque
class EulerGraph:
"""Euler yolu ve devresi algoritmaları"""
def __init__(self, directed=False):
self.graph = defaultdict(list)
self.directed = directed
def add_edge(self, u, v):
self.graph[u].append(v)
if not self.directed:
self.graph[v].append(u)
def degree(self, v):
"""Düğümün derecesi (yönsüz graf için)"""
return len(self.graph[v])
def is_connected(self):
"""Graf bağlı mı? (BFS ile kontrol)"""
if not self.graph:
return True
start = next(iter(self.graph))
visited = set()
queue = deque([start])
while queue:
node = queue.popleft()
if node in visited:
continue
visited.add(node)
for neighbor in self.graph[node]:
if neighbor not in visited:
queue.append(neighbor)
return len(visited) == len(self.graph)
def has_euler_path(self):
"""Euler yolu var mı?"""
if not self.is_connected():
return False, "Graf bağlı değil"
odd_degree_count = sum(1 for v in self.graph if self.degree(v) % 2 == 1)
if odd_degree_count == 0:
return True, "Euler DEVRESİ var (tüm dereceler çift)"
elif odd_degree_count == 2:
return True, "Euler YOLU var (2 tek dereceli düğüm)"
else:
return False, f"Euler yolu yok ({odd_degree_count} tek dereceli düğüm)"
def find_euler_path(self):
"""
Hierholzer algoritması ile Euler yolu bul
"""
has_path, msg = self.has_euler_path()
if not has_path:
return None, msg
# Çalışma kopyası
temp_graph = defaultdict(list)
for v in self.graph:
temp_graph[v] = self.graph[v].copy()
# Başlangıç: tek dereceli varsa oradan, yoksa herhangi birinden
start = None
for v in temp_graph:
if len(temp_graph[v]) % 2 == 1:
start = v
break
if start is None:
start = next(iter(temp_graph))
stack = [start]
path = []
while stack:
v = stack[-1]
if temp_graph[v]:
u = temp_graph[v].pop()
# Yönsüz graf için karşı kenarı da sil
if not self.directed:
temp_graph[u].remove(v)
stack.append(u)
else:
path.append(stack.pop())
return path[::-1], msg
# Örnek: Königsberg Köprüleri benzeri
print("=== EULER YOLU ANALİZİ ===\n")
# Örnek 1: Euler devresi olan graf
g1 = EulerGraph()
edges1 = [(0, 1), (1, 2), (2, 3), (3, 0), (0, 2)]
for u, v in edges1:
g1.add_edge(u, v)
print("Graf 1 (kare + çapraz):")
print("Kenarlar:", edges1)
has_path, msg = g1.has_euler_path()
print(f"Sonuç: {msg}")
if has_path:
path, _ = g1.find_euler_path()
print(f"Euler yolu: {' → '.join(map(str, path))}")
print("\n" + "="*40 + "\n")
# Örnek 2: Euler yolu olan graf (devre değil)
g2 = EulerGraph()
edges2 = [(0, 1), (1, 2), (2, 0), (2, 3)]
for u, v in edges2:
g2.add_edge(u, v)
print("Graf 2 (üçgen + kuyruk):")
print("Kenarlar:", edges2)
has_path, msg = g2.has_euler_path()
print(f"Sonuç: {msg}")
if has_path:
path, _ = g2.find_euler_path()
print(f"Euler yolu: {' → '.join(map(str, path))}")
def find_hamiltonian_path(graph, n):
"""
Hamilton yolu bul - Backtracking
⚠️ Karmaşıklık: O(n!) - NP-Complete problem!
Küçük graflar için uygundur.
"""
def backtrack(path, visited):
# Tüm düğümler ziyaret edildi mi?
if len(path) == n:
return path.copy()
current = path[-1]
for neighbor in graph[current]:
if neighbor not in visited:
visited.add(neighbor)
path.append(neighbor)
result = backtrack(path, visited)
if result:
return result
# Backtrack
path.pop()
visited.remove(neighbor)
return None
# Her düğümden başlamayı dene
for start in range(n):
path = [start]
visited = {start}
result = backtrack(path, visited)
if result:
return result
return None
def find_hamiltonian_cycle(graph, n):
"""Hamilton devresi bul (başlangıca dönmeli)"""
def backtrack(path, visited):
if len(path) == n:
# Son düğümden başlangıca kenar var mı?
if path[0] in graph[path[-1]]:
return path + [path[0]]
return None
current = path[-1]
for neighbor in graph[current]:
if neighbor not in visited:
visited.add(neighbor)
path.append(neighbor)
result = backtrack(path, visited)
if result:
return result
path.pop()
visited.remove(neighbor)
return None
path = [0]
visited = {0}
return backtrack(path, visited)
# Test
print("=== HAMİLTON YOLU ANALİZİ ===\n")
# Graf: 5 düğümlü pentagon
n = 5
graph = {
0: [1, 4],
1: [0, 2],
2: [1, 3],
3: [2, 4],
4: [3, 0]
}
print("Graf: Pentagon (5 düğüm)")
print("Kenarlar: 0-1, 1-2, 2-3, 3-4, 4-0\n")
path = find_hamiltonian_path(graph, n)
if path:
print(f"✅ Hamilton Yolu: {' → '.join(map(str, path))}")
else:
print("❌ Hamilton yolu bulunamadı")
cycle = find_hamiltonian_cycle(graph, n)
if cycle:
print(f"✅ Hamilton Devresi: {' → '.join(map(str, cycle))}")
else:
print("❌ Hamilton devresi bulunamadı")
print("\n" + "="*40 + "\n")
# Daha karmaşık graf
n2 = 4
graph2 = {
0: [1, 2],
1: [0, 2, 3],
2: [0, 1, 3],
3: [1, 2]
}
print("Graf 2: Tam dörtgen (K4)")
path2 = find_hamiltonian_path(graph2, n2)
cycle2 = find_hamiltonian_cycle(graph2, n2)
print(f"Hamilton Yolu: {' → '.join(map(str, path2)) if path2 else 'Yok'}")
print(f"Hamilton Devresi: {' → '.join(map(str, cycle2)) if cycle2 else 'Yok'}")
Bir satıcı, tüm şehirleri tam olarak bir kez ziyaret edip başlangıç noktasına dönmek istiyor. En kısa toplam mesafeyi bulmak istiyoruz.
Kargo dağıtım rotası optimizasyonu (UPS, FedEx)
CNC makinelerinde delik delme sırası
DNA sıralama ve genom assembly
Veri paketlerinin optimal yönlendirilmesi
from itertools import permutations
import time
def tsp_brute_force(dist_matrix):
"""
TSP - Brute Force (Kaba Kuvvet)
Tüm permütasyonları dene: O(n!)
Sadece küçük n için (n ≤ 10)
"""
n = len(dist_matrix)
cities = list(range(1, n)) # 0'dan başla, diğerlerini permüte et
min_cost = float('inf')
best_path = None
for perm in permutations(cities):
path = [0] + list(perm) + [0]
cost = sum(dist_matrix[path[i]][path[i+1]] for i in range(n))
if cost < min_cost:
min_cost = cost
best_path = path
return best_path, min_cost
def tsp_dp(dist_matrix):
"""
TSP - Dinamik Programlama (Held-Karp)
Karmaşıklık: O(n² × 2ⁿ)
Brute force'dan çok daha hızlı ama hala exponential
"""
n = len(dist_matrix)
# dp[mask][i] = mask kümesindeki şehirleri ziyaret edip i'de biten min maliyet
# mask: hangi şehirlerin ziyaret edildiğini gösteren bitmask
INF = float('inf')
dp = [[INF] * n for _ in range(1 << n)]
parent = [[-1] * n for _ in range(1 << n)]
# Başlangıç: 0'dan başla
dp[1][0] = 0
for mask in range(1 << n):
for last in range(n):
if dp[mask][last] == INF:
continue
if not (mask & (1 << last)):
continue
for next_city in range(n):
if mask & (1 << next_city):
continue # Zaten ziyaret edilmiş
new_mask = mask | (1 << next_city)
new_cost = dp[mask][last] + dist_matrix[last][next_city]
if new_cost < dp[new_mask][next_city]:
dp[new_mask][next_city] = new_cost
parent[new_mask][next_city] = last
# Tüm şehirler ziyaret edildi, 0'a dön
full_mask = (1 << n) - 1
min_cost = INF
last_city = -1
for i in range(1, n):
cost = dp[full_mask][i] + dist_matrix[i][0]
if cost < min_cost:
min_cost = cost
last_city = i
# Yolu reconstruct et
path = [0]
mask = full_mask
current = last_city
while current != 0:
path.append(current)
prev = parent[mask][current]
mask ^= (1 << current)
current = prev
path.append(0)
path.reverse()
return path, min_cost
# Örnek: 5 şehir
print("=== GEZGİN SATICI PROBLEMİ ===\n")
# Mesafe matrisi (şehirler arası mesafeler)
dist = [
[0, 10, 15, 20, 25],
[10, 0, 35, 25, 30],
[15, 35, 0, 30, 20],
[20, 25, 30, 0, 15],
[25, 30, 20, 15, 0]
]
city_names = ["Ankara", "İstanbul", "İzmir", "Bursa", "Antalya"]
print("Şehirler:", city_names)
print("\nMesafe Matrisi:")
print(" ", " ".join(f"{c[:3]:>5}" for c in city_names))
for i, row in enumerate(dist):
print(f"{city_names[i][:3]:>5}:", " ".join(f"{d:>5}" for d in row))
print("\n--- Brute Force Çözüm ---")
start = time.time()
path_bf, cost_bf = tsp_brute_force(dist)
time_bf = time.time() - start
print(f"En iyi rota: {' → '.join(city_names[i] for i in path_bf)}")
print(f"Toplam mesafe: {cost_bf}")
print(f"Süre: {time_bf*1000:.2f} ms")
print("\n--- Dinamik Programlama (Held-Karp) ---")
start = time.time()
path_dp, cost_dp = tsp_dp(dist)
time_dp = time.time() - start
print(f"En iyi rota: {' → '.join(city_names[i] for i in path_dp)}")
print(f"Toplam mesafe: {cost_dp}")
print(f"Süre: {time_dp*1000:.2f} ms")
def tsp_greedy(dist_matrix):
"""
TSP - Greedy (Açgözlü) Yaklaşım
Her adımda en yakın ziyaret edilmemiş şehre git
Karmaşıklık: O(n²)
Optimal değil ama hızlı!
"""
n = len(dist_matrix)
visited = [False] * n
path = [0]
visited[0] = True
total_cost = 0
for _ in range(n - 1):
current = path[-1]
nearest = -1
min_dist = float('inf')
for next_city in range(n):
if not visited[next_city] and dist_matrix[current][next_city] < min_dist:
min_dist = dist_matrix[current][next_city]
nearest = next_city
path.append(nearest)
visited[nearest] = True
total_cost += min_dist
# Başlangıca dön
total_cost += dist_matrix[path[-1]][0]
path.append(0)
return path, total_cost
def tsp_2opt(dist_matrix, initial_path=None):
"""
TSP - 2-Opt İyileştirme
Mevcut bir rotayı 2 kenarı değiştirerek iyileştir
Local optimum'a ulaşana kadar tekrarla
"""
n = len(dist_matrix)
if initial_path is None:
path, _ = tsp_greedy(dist_matrix)
else:
path = initial_path.copy()
def calculate_cost(p):
return sum(dist_matrix[p[i]][p[i+1]] for i in range(len(p)-1))
improved = True
iterations = 0
while improved:
improved = False
iterations += 1
for i in range(1, n - 1):
for j in range(i + 1, n):
# 2-opt swap: i ile j arasını ters çevir
new_path = path[:i] + path[i:j+1][::-1] + path[j+1:]
new_cost = calculate_cost(new_path)
old_cost = calculate_cost(path)
if new_cost < old_cost:
path = new_path
improved = True
return path, calculate_cost(path), iterations
# Daha büyük örnek: 8 şehir
print("=== TSP YAKLAŞIK ÇÖZÜMLER ===\n")
import random
random.seed(42)
n = 8
# Rastgele mesafe matrisi (simetrik)
dist = [[0] * n for _ in range(n)]
for i in range(n):
for j in range(i+1, n):
d = random.randint(10, 100)
dist[i][j] = d
dist[j][i] = d
print(f"Şehir sayısı: {n}")
print(f"Brute force permütasyon sayısı: {n}! = {__import__('math').factorial(n):,}")
print("\n--- Greedy Çözüm ---")
path_g, cost_g = tsp_greedy(dist)
print(f"Rota: {' → '.join(map(str, path_g))}")
print(f"Maliyet: {cost_g}")
print("\n--- 2-Opt İyileştirme ---")
path_2opt, cost_2opt, iters = tsp_2opt(dist)
print(f"Rota: {' → '.join(map(str, path_2opt))}")
print(f"Maliyet: {cost_2opt}")
print(f"İterasyon: {iters}")
improvement = (cost_g - cost_2opt) / cost_g * 100
print(f"\n📈 2-Opt iyileştirmesi: %{improvement:.1f} daha iyi")
Yazılım mülakatlarda sıkça karşılaşılan graf problemleri. Önce kendiniz çözmeyi deneyin!
2D bir grid verildiğinde, "1" kara ve "0" su temsil eder. Birbirine bağlı (4 yönde) kara parçalarından oluşan ada sayısını bulun. (LeetCode #200)
numCourses ders ve prerequisites ön koşul listesi verildiğinde, tüm dersleri bitirebilir misiniz? (LeetCode #207)
beginWord'den endWord'e dönüşmek için gereken minimum adım sayısını bulun. Her adımda sadece 1 harf değiştirebilirsiniz ve ara kelimeler wordList'te olmalı. (LeetCode #127)
Bir grid'de 0=boş, 1=taze portakal, 2=çürük portakal. Her dakika çürük portakallar komşu taze portakalları çürütür. Tüm portakalların çürümesi için minimum dakika bulun. (LeetCode #994)
Verilen bir grafın derin kopyasını (deep clone) oluşturun. (LeetCode #133)
| Problem | Algoritma | Neden? |
|---|---|---|
| En kısa yol (ağırlıksız) | BFS | Seviye sırasında arar, O(V+E) |
| En kısa yol (ağırlıklı) | Dijkstra | Pozitif ağırlıklar, O((V+E)logV) |
| Bağımlılık sıralaması | Topological Sort | DAG'larda görev sırası, O(V+E) |
| Döngü tespiti | DFS | Back edge kontrolü, O(V+E) |
| Bağlantılı bileşenler | BFS/DFS | Her ikisi de çalışır, O(V+E) |
| Tüm çiftler en kısa yol | Floyd-Warshall | Dinamik programlama, O(V³) |
| Minimum yayılan ağaç | Kruskal / Prim | Greedy yaklaşım, O(E log E) |
| Euler yolu/devresi | Hierholzer | Derece kontrolü + DFS, O(E) |
| Hamilton yolu/devresi | Backtracking | NP-Complete, O(n!) |
| Gezgin satıcı (TSP) | DP / Greedy+2-Opt | Optimal O(n²2ⁿ), yaklaşık O(n²) |