🌳 3.6: BFS - Genişlik Öncelikli Arama

Breadth-First Search: Queue'nun En Güçlü Uygulaması

📚 Bu Konuyu Anlamak İçin

BFS algoritması Graf veri yapısı üzerinde çalışır. Graf konusu detaylı olarak Bölüm 11: Graf Veri Yapısı'nda işlenmektedir. Ayrıca ağaç yapıları için Bölüm 5: Ağaç (Tree) Veri Yapısı'na bakabilirsiniz.

Bu sayfada graf kavramının temellerini öğreneceksiniz. BFS, Queue veri yapısının en önemli uygulamalarından biridir.

📢 Graf Kavramını Tanıyalım

BFS algoritmasını anlamak için önce graf kavramını bilmemiz gerekiyor:

Graf Nedir?

Graf, düğümler (nodes/vertices) ve bu düğümleri birbirine bağlayan kenarlardan (edges) oluşan matematiksel bir yapıdır.

A B C D E F

6 düğüm (A-F) ve 7 kenar içeren örnek bir graf

Gerçek Hayat Örnekleri

  • Sosyal Ağlar: Kişiler düğüm, arkadaşlıklar kenar (Instagram takipçileri, Facebook arkadaşları)
  • Şehir Haritaları: Şehirler düğüm, yollar kenar (GPS navigasyon)
  • Web Sayfaları: Sayfalar düğüm, linkler kenar (Google'ın PageRank algoritması)
  • Oyun Haritaları: Odalar düğüm, kapılar/yollar kenar

Komşuluk Listesi (Adjacency List)

Grafı bilgisayarda temsil etmenin en yaygın yolu komşuluk listesidir. Her düğüm için, ona doğrudan bağlı olan düğümlerin listesini tutarız:

A ['B', 'D']
B ['A', 'C', 'E']
C ['B', 'F']
D ['A', 'E']
E ['B', 'D', 'F']
F ['C', 'E']

Her düğümün rengi, yukarıdaki graf görselindeki rengine karşılık gelir

🤔 BFS Nedir?

Breadth-First Search (Genişlik Öncelikli Arama), bir graf veya ağaç yapısında düğümleri seviye seviye gezerek arama yapan algoritmadır.

Neden "Genişlik Öncelikli"?

BFS, başlangıç noktasından itibaren önce tüm komşuları ziyaret eder, sonra komşuların komşularına geçer. Yani grafı "yatay" olarak tarar:

  • Seviye 0: Başlangıç düğümü
  • Seviye 1: Başlangıcın tüm komşuları
  • Seviye 2: Komşuların komşuları
  • ... ve böyle devam eder

BFS ve Queue İlişkisi

BFS algoritması Queue (FIFO) veri yapısını kullanır. Bu sayede ilk keşfedilen düğümler ilk işlenir ve seviye seviye ilerleme sağlanır.

Eğer Stack kullanılsaydı, bu algoritma DFS (Depth-First Search - Derinlik Öncelikli Arama) olurdu.

🎮 BFS Algoritması Akışı

BAŞLA Başlangıç düğümünü Queue'ya ekle Queue boş mu? EVET BİTTİ HAYIR Dequeue: Düğümü çıkar Düğümü işle / ziyaret et Ziyaret edilmemiş komşuları Queue'ya ekle tekrarla

💻 Python İmplementasyonu

Python Kodu
from collections import deque

def bfs(graf, baslangic):
    """
    BFS (Breadth-First Search) - Genişlik Öncelikli Arama

    Graf üzerinde başlangıç düğümünden başlayarak
    tüm ulaşılabilir düğümleri seviye seviye ziyaret eder.

    Parametreler:
        graf: Komşuluk listesi (dictionary)
        baslangic: Başlangıç düğümü

    Returns:
        Ziyaret edilen düğümlerin listesi (ziyaret sırasına göre)
    """
    # Ziyaret edilen düğümleri takip eden küme
    ziyaret_edildi = set()

    # Queue: İşlenecek düğümleri tutar (FIFO)
    kuyruk = deque([baslangic])

    # Başlangıcı ziyaret edildi olarak işaretle
    ziyaret_edildi.add(baslangic)

    # Ziyaret sırasını kaydet
    ziyaret_sirasi = []

    print(f"🚀 BFS Başlıyor - Başlangıç: {baslangic}")
    print("=" * 50)

    while kuyruk:  # Queue boş olana kadar devam et
        # Queue'dan (baştan) bir düğüm çıkar
        dugum = kuyruk.popleft()
        ziyaret_sirasi.append(dugum)

        print(f"📍 Ziyaret: {dugum}")
        print(f"   Kuyruk: {list(kuyruk)}")

        # Bu düğümün tüm komşularını kontrol et
        for komsu in graf[dugum]:
            if komsu not in ziyaret_edildi:
                # Henüz ziyaret edilmemişse kuyruğa ekle
                ziyaret_edildi.add(komsu)
                kuyruk.append(komsu)
                print(f"   ➕ Kuyruğa eklendi: {komsu}")

        print()

    return ziyaret_sirasi

# Örnek graf (komşuluk listesi)
graf = {
    'A': ['B', 'C'],
    'B': ['A', 'D', 'E'],
    'C': ['A', 'F'],
    'D': ['B'],
    'E': ['B', 'F'],
    'F': ['C', 'E']
}

print("📊 Graf Yapısı:")
for dugum, komsular in graf.items():
    print(f"   {dugum} → {komsular}")
print()

# BFS'i çalıştır
sonuc = bfs(graf, 'A')

print("=" * 50)
print(f"✅ Ziyaret Sırası: {' → '.join(sonuc)}")

🗺️ En Kısa Yol Bulma (Labirent)

BFS'in en güçlü özelliği: ağırlıksız graflarda en kısa yolu garanti eder!

Neden BFS En Kısa Yolu Bulur?

BFS seviye seviye ilerlediği için, bir düğüme ilk ulaştığımızda en az adımla ulaşmış oluruz. Çünkü daha önce o düğüme ulaşan bir yol olsaydı, o yol daha önceki bir seviyede keşfedilirdi.

Python Kodu
from collections import deque

def bfs_en_kisa_yol(labirent, baslangic, hedef):
    """
    2D labirentte BFS ile en kısa yolu bulur.

    Labirent:
        0 = geçilebilir hücre
        1 = duvar

    Returns:
        En kısa yol (koordinat listesi) veya None
    """
    satirlar = len(labirent)
    sutunlar = len(labirent[0])

    # 4 yön: yukarı, aşağı, sol, sağ
    yonler = [(-1, 0), (1, 0), (0, -1), (0, 1)]

    # Queue: (satir, sutun, yol)
    kuyruk = deque([(baslangic[0], baslangic[1], [baslangic])])

    # Ziyaret edilen hücreler
    ziyaret_edildi = {baslangic}

    while kuyruk:
        satir, sutun, yol = kuyruk.popleft()

        # Hedefe ulaştık mı?
        if (satir, sutun) == hedef:
            return yol

        # 4 yöne bak
        for ds, dc in yonler:
            yeni_satir = satir + ds
            yeni_sutun = sutun + dc
            yeni_konum = (yeni_satir, yeni_sutun)

            # Sınırlar içinde mi?
            if 0 <= yeni_satir < satirlar and 0 <= yeni_sutun < sutunlar:
                # Duvar değil mi ve ziyaret edilmemiş mi?
                if labirent[yeni_satir][yeni_sutun] == 0 and yeni_konum not in ziyaret_edildi:
                    ziyaret_edildi.add(yeni_konum)
                    kuyruk.append((yeni_satir, yeni_sutun, yol + [yeni_konum]))

    return None  # Yol bulunamadı

# Örnek labirent
# S = Start (Başlangıç), E = End (Hedef)
# 0 = Geçilebilir, 1 = Duvar
labirent = [
    [0, 0, 0, 1, 0],  # S . . # .
    [1, 1, 0, 1, 0],  # # # . # .
    [0, 0, 0, 0, 0],  # . . . . .
    [0, 1, 1, 1, 0],  # . # # # .
    [0, 0, 0, 0, 0],  # . . . . E
]

baslangic = (0, 0)  # Sol üst köşe
hedef = (4, 4)      # Sağ alt köşe

print("🗺️ Labirent (0=yol, 1=duvar):")
for i, satir in enumerate(labirent):
    satir_str = ""
    for j, hucre in enumerate(satir):
        if (i, j) == baslangic:
            satir_str += "S "
        elif (i, j) == hedef:
            satir_str += "E "
        elif hucre == 1:
            satir_str += "█ "
        else:
            satir_str += ". "
    print(f"   {satir_str}")

print()

# En kısa yolu bul
yol = bfs_en_kisa_yol(labirent, baslangic, hedef)

if yol:
    print(f"✅ En kısa yol bulundu! ({len(yol)-1} adım)")
    print(f"   Yol: {' → '.join([str(k) for k in yol])}")

    # Yolu labirent üzerinde göster
    print("\n🎯 Çözüm:")
    for i, satir in enumerate(labirent):
        satir_str = ""
        for j, hucre in enumerate(satir):
            if (i, j) == baslangic:
                satir_str += "S "
            elif (i, j) == hedef:
                satir_str += "E "
            elif (i, j) in yol:
                satir_str += "* "
            elif hucre == 1:
                satir_str += "█ "
            else:
                satir_str += ". "
        print(f"   {satir_str}")
else:
    print("❌ Yol bulunamadı!")

📊 BFS vs DFS Karşılaştırması

Özellik BFS (Queue) DFS (Stack)
Veri Yapısı Queue (FIFO) Stack (LIFO)
Zaman O(V + E) O(V + E)
Bellek O(V) - Daha fazla O(h) - Daha az
En Kısa Yol ✅ Garanti ❌ Garanti yok
Kullanım Sosyal ağ, GPS, Oyun AI Labirent, Puzzle, Backtracking