0Pricing
Coding Interview Prep · Ders

Geri İzleme Şablonu: Seç, Keşfet, Seçimi Geri Al

Üç adımlı geri izleme iskeletini uygulayın, küçük bir örnek üzerinde izini sürün ve budama koşullarının nereye yerleştirileceğini belirleyin.

Geri İzleme Şablonu: Seç, Keşfet, Seçimi Geri Al, CoddyKit'te ücretsiz bir Coding Interview Prep dersidir. Bu, 4 dersinin 1. dersidir. Aşağıdan dersin tamamını ücretsiz okuyabilir, sonra tarayıcıda yerleşik kod editörü ve 7/24 yapay zeka koçu ile uygulamalı olarak pratik yapabilirsin. Bu, Coding Interview Prep öğrenme yolunun bir parçasıdır ve ilerlemeniz web ve CoddyKit uygulaması arasında senkronize olur. Coding Interview Prep kursu toplamda 4 dersten oluşur.

Geri İzleme Nedir

Geri izleme, her adayı artımlı olarak inceleyerek tüm (veya bazı) solution seçeneklerini bulmaya yönelik sistematik bir yöntemdir; bir dalın geçerli bir solution üretemeyeceği anlaşılır anlaşılmaz o dalı (budama) bırakır. Sudoku çözmenin, permütasyon üretmenin ve tüm geçerli kombinasyonları bulmanın temelindeki algoritmadır. Bunu, bir karar ağacında derinlik öncelikli arama olarak düşünebilirsiniz.

# Mental model: backtracking explores a decision tree
# At each node you make a choice, go deeper, then undo it
#
# Tree for generating subsets of [1,2,3]:
#        []
#      /    \
#    [1]   []
#   / \    / \
# [1,2][1][2] []
# ...

# Every leaf is a potential solution
# Pruning cuts branches early based on constraints
print('Backtracking = DFS on decision tree with pruning')

Üç Adımlı Şablon

Her geri izleme işlevi üç adımı izler: Seç — kullanılabilir seçeneklerden sonraki adayı seçin. İncele — bu seçimle özyinelemeli çağrı yaparak karar ağacında bir seviye daha derine inin. Seçimi Geri Al — sonraki aday için durumu eski hâline getirmek üzere özyinelemeden döndükten sonra seçimi geri alın. Bu örüntü, farklı bağlamlarda ekle/özyinele/çıkar veya işaretle/özyinele/işareti kaldır olarak da adlandırılır.

def backtrack(current_state, choices, results):
    # Base case: is current_state a complete solution?
    if is_complete(current_state):
        results.append(list(current_state))  # record solution
        return
    
    for choice in choices:
        if is_valid(choice, current_state):    # pruning condition
            # 1. CHOOSE
            current_state.append(choice)
            # 2. EXPLORE
            backtrack(current_state, choices, results)
            # 3. UNCHOOSE (backtrack)
            current_state.pop()

# Placeholder functions — filled per problem
def is_complete(state): return True
def is_valid(choice, state): return True

En Basit Örnek: Tüm Alt Kümeler

[1, 2, 3] dizisinin tüm subsets kümelerini üretin. Her dizinde öğeyi dâhil etmeyi veya hariç tutmayı seçeriz. Her çağrıdan sonra başlangıç dizini ilerletilir; böylece önceki öğeleri yeniden ziyaret etmeyiz. Her kısmi durum geçerli olduğundan herhangi bir kısıt denetimi gerekmez. Bu işlem 2ⁿ subsets üretir. Seçimi geri alma adımı, özyinelemeli çağrıdan sonra uygulanan path.pop() işlemidir.

def subsets(nums):
    result = []
    def backtrack(start, path):
        result.append(list(path))  # every state is a valid subset
        for i in range(start, len(nums)):
            path.append(nums[i])    # CHOOSE
            backtrack(i + 1, path)  # EXPLORE
            path.pop()              # UNCHOOSE
    backtrack(0, [])
    return result

print(subsets([1, 2, 3]))
# [[], [1], [1,2], [1,2,3], [1,3], [2], [2,3], [3]]

Budama Koşulunu Belirleme

Geri izlemenin kaba kuvvete göre gücü budamadan gelir: kısmi bir yolun geçerli bir solution'a götüremeyeceğini erkenden fark etmek. Kombinasyon toplamında (bütçeli hedef toplamı), çalışan toplam hedefi aşar aşmaz daha derindeki herhangi bir dal yalnızca büyüyeceğinden hemen dönerek budayın. N vezir probleminde bir vezir mevcut vezirlere saldırıyorsa o sütunu atlayın. Budama, üstel ağaçları yönetilebilir aramalara dönüştürür.

def combination_sum(candidates, target):
    result = []
    candidates.sort()  # sort enables early termination
    def backtrack(start, path, remaining):
        if remaining == 0:
            result.append(list(path))
            return
        for i in range(start, len(candidates)):
            c = candidates[i]
            if c > remaining: break   # PRUNE: sorted, so rest are bigger too
            path.append(c)            # CHOOSE
            backtrack(i, path, remaining - c)   # EXPLORE (reuse allowed)
            path.pop()                # UNCHOOSE
    backtrack(0, [], target)
    return result

print(combination_sum([2, 3, 6, 7], 7))  # [[2,2,3],[7]]

Durumu Geri Yüklemek Kritiktir

Geri izlemede sık görülen bir hata, sonraki yinelemeden önce durumu tamamen geri yüklememektir. Değiştirilebilir bir veri yapısı (liste, küme veya ızgara) kullanıyorsanız Seç sırasında yapılan her değişiklik Seçimi Geri Al adımında tersine çevrilmelidir. Örneğin bir ızgarayı (Sudoku veya Kelime Arama gibi) değiştirirken özyinelemeli çağrıdan sonra hücreyi boş olarak ayarlayın. Bunu unutmak, kardeş dallar için durumu bozuk bırakır.

# Bug: forgetting to unmark in word search
# Correct pattern for grid backtracking:
def word_search(board, word):
    m, n = len(board), len(board[0])
    def dfs(r, c, k):
        if k == len(word): return True
        if not (0<=r<m and 0<=c<n): return False
        if board[r][c] != word[k]: return False
        temp, board[r][c] = board[r][c], '#'  # CHOOSE (mark visited)
        found = any(dfs(r+dr, c+dc, k+1)
                    for dr, dc in [(0,1),(0,-1),(1,0),(-1,0)])
        board[r][c] = temp  # UNCHOOSE (restore cell)
        return found
    return any(dfs(r, c, 0) for r in range(m) for c in range(n))

board = [['A','B','C','E'],['S','F','C','S'],['A','D','E','E']]
print(word_search([row[:] for row in board], 'ABCCED'))  # True

Karar Ağacını İzleme

[2, 3, 6, 7] ve 7 hedefiyle kombinasyon toplamı için ağacı izleyin: kökte 2'yi deneyin. 2'den sonra 2'yi tekrar deneyin (kalan=3). 2+2'den sonra 2'yi tekrar deneyin (kalan=1). 2>1 olduğundan budayın. 3'ü deneyin: 3>1, budayın. Geri izleyin. 2+2'den sonra 3'ü deneyin (kalan=3). 3 kalanla eşleşir: [2,2,3] kaydedin. Geri izleyin ve devam edin. Bu iz, geçersiz sonuçlar üretmeden önce budamanın dalları nasıl ortadan kaldırdığını gösterir.

def combination_sum_trace(candidates, target):
    result = []
    candidates.sort()
    def backtrack(start, path, remaining, depth):
        indent = '  ' * depth
        print(f'{indent}explore({path}, remaining={remaining})')
        if remaining == 0:
            result.append(list(path))
            print(f'{indent}FOUND: {path}')
            return
        for i in range(start, len(candidates)):
            c = candidates[i]
            if c > remaining:
                print(f'{indent}PRUNE at {c}')
                break
            path.append(c)
            backtrack(i, path, remaining - c, depth + 1)
            path.pop()
    backtrack(0, [], target, 0)
    return result

combination_sum_trace([2, 3, 6, 7], 7)

Geri İzleme ve Kaba Kuvvet

Kaba kuvvet, olası tüm eksiksiz solution'ları dener ve ardından her birini doğrular. Geri izleme ise oluşturma sırasında budama yapar ve geçersiz yolları hiçbir zaman tamamlamaz. N=8 olan N vezir probleminde kaba kuvvet 8^8 = 16 milyon yerleştirmeyi denetler. Geri izleme bunu yaklaşık 2.057 özyinelemeli çağrıya indirir. N büyüdükçe fark çarpıcı biçimde artar: N=12 için kaba kuvvet 8,9 milyar yerleştirmeyi denerken geri izleme ağacın yalnızca küçük bir bölümünü inceler.

# Compare call counts: brute force vs backtracking for permutations
import sys
calls_brute = [0]
calls_back = [0]

def brute_force_perms(nums):
    from itertools import permutations
    return list(permutations(nums))

def backtrack_perms(nums):
    result = []
    used = [False] * len(nums)
    def bt(path):
        calls_back[0] += 1
        if len(path) == len(nums):
            result.append(list(path))
            return
        for i, n in enumerate(nums):
            if not used[i]:
                used[i] = True
                path.append(n)
                bt(path)
                path.pop()
                used[i] = False
    bt([])
    return result

backtrack_perms([1,2,3,4])
print(f'Backtrack calls for 4 items: {calls_back[0]}')

Toplamak mı Erken Dönmek mi

Geri izleme problemleri iki kategoriye ayrılır: tüm solution'ları listelemek (tamamlanan her yolu toplamak) veya herhangi bir solution bulmak (bir yol başarılı olur olmaz True döndürmek). Listeleme için her zaman sonuç listesine append uygulayın. Herhangi birini bulma durumunda özyinelemeli çağrıdan hemen True döndürün ve bunu üst çağrılara aktarın. any(backtrack(...)) döndürmek veya if backtrack(...): return True kullanmak kısa devre davranışını uygular.

# Enumerate all: collect in results list
def all_solutions(candidates):
    results = []
    def bt(path, remaining):
        if remaining == 0:
            results.append(list(path))
            return
        for c in candidates:
            if c <= remaining:
                path.append(c); bt(path, remaining - c); path.pop()
    bt([], 5)
    return results

# Find any one: return True on first success
def any_solution(candidates, target):
    def bt(path, remaining):
        if remaining == 0: return True
        for c in candidates:
            if c <= remaining:
                path.append(c)
                if bt(path, remaining - c): return True  # short-circuit
                path.pop()
        return False
    path = []
    return bt(path, target), path

Geri İzlemeyle Önbellekleme

Saf geri izleme her yolu önbelleğe almadan inceler; tüm solution'lar gerektiğinde bu yaklaşım uygundur. Ancak bazı geri izleme problemlerinde örtüşen alt problemler bulunur. Örneğin Kelime Bölme II, geri izleme + önbellekleme ile çözülebilir: her başlangıç dizininden oluşturulabilecek cümleler listesini önbelleğe alın. Bu, en kötü durumdaki üstel geri izlemeyi polinom zamanlı bir algoritmaya dönüştürür. Bu birleşik yaklaşımı uygulamak için alt problemlerin ne zaman tekrarlandığını fark edin.

from functools import lru_cache

def word_break_all(s, wordDict):
    words = set(wordDict)
    
    @lru_cache(maxsize=None)
    def bt(start):
        if start == len(s): return ['']  # empty suffix
        result = []
        for end in range(start + 1, len(s) + 1):
            word = s[start:end]
            if word in words:
                for rest in bt(end):
                    result.append(word if not rest else word + ' ' + rest)
        return result
    
    return bt(0)

print(word_break_all('catsanddog', ['cat','cats','and','sand','dog']))
# ['cat sand dog', 'cats and dog']

Geri İzlemenin Zaman Karmaşıklığı

Geri izlemenin zaman karmaşıklığı, karar ağacındaki yaprakların sayısının düğüm başına yapılan işle çarpımına bağlıdır. subsets için: O(n × 2ⁿ). Permütasyonlar için: O(n × n!). Kombinasyon toplamı için: en kötü durumda O(target/min_candidate ^ n). Budama sabiti azaltır, ancak asimptotik sınırı değiştirmez. Bir mülakatta karmaşıklık sorulduğunda en kötü durumdaki ağaç boyutunu belirtin ve budamanın uygulamada genellikle algoritmayı çok daha hızlı hâle getirdiğini ekleyin.

# Complexity quick reference:
# Subsets of n elements:     O(n * 2^n)  - 2^n subsets, each copied in O(n)
# Permutations of n:          O(n * n!)   - n! perms, each copied in O(n)
# Combination sum (target T): O(T^n / n!) worst case without pruning
# N-Queens:                   O(n!)       - prune reduces practical count

# For n=10 permutations: 10! = 3,628,800 paths
import math
n = 10
print(f'n={n}: n!={math.factorial(n):,} paths')
print(f'n={n}: 2^n={2**n:,} subsets')

Geri İzleme Problemlerini Belirleme

Bir problemin geri izleme gerektirdiğini gösteren işaretler şunlardır: (1) Kombinasyonları, permütasyonları veya subsets yapılarını bulmanın ya da tümünü üretmenin istenmesi. (2) Problemin, kısıtlar altında öğeleri veya kişileri yerleştirmeyi içermesi (N vezir, Sudoku). (3) solution uzayının üstel olması, ancak kısıtların dalların çoğunu erkenden elemesi. (4) Bir graf veya ızgaradaki, durumları yeniden ziyaret edebilecek yolları incelemeniz gerekmesi. Bu işaretleri gördüğünüzde Seç-İncele-Seçimi Geri Al şablonuna başvurun.

# Common backtracking problem types:
# 1. Subsets / Power set
# 2. Permutations (with/without duplicates)
# 3. Combinations (k from n, combination sum)
# 4. Grid path finding (word search, unique paths with visited tracking)
# 5. Constraint satisfaction (N-queens, Sudoku solver)
# 6. String partitioning (palindrome partition, word break all)

# Template reminder:
def backtrack(start, path):
    # base case: add to results or return True
    for choice in get_choices(start):
        if is_valid(choice, path):   # prune
            path.append(choice)      # choose
            backtrack(start+1, path) # explore
            path.pop()               # unchoose

def get_choices(start): return []
def is_valid(c, p): return True

Hızlı Kontrol

Bu dersteki Veri Yapıları & Algoritmalar — Kodlama Mülakatına Hazırlık kavramlarını anlayışınızı sınayın.

Ders Özeti

Bu derste şunları öğrendiniz: the geri izleme şablonu üç adımdan oluşur — seç, incele, seçimi geri al — ve bunlar bir seçimi eklemeye, özyinelemeli çağrı yapmaya ve seçimi kaldırmaya karşılık gelir, budama koşulları dalları erkenden eler ve geri izlemeyi kaba kuvvete kıyasla uygulanabilir kılan şey bunlardır ve kardeş dalların durumunu bozmamak için her özyinelemeli çağrıdan sonra durum tamamen geri yüklenmelidir. Sırada şablonu tüm Alt Kümeleri ve Kuvvet Kümesi'ni üretmek için uygulayacağız.

Sıkça Sorulan Sorular

“Geri İzleme Şablonu: Seç, Keşfet, Seçimi Geri Al” dersi ücretsiz mi?

Evet — “Geri İzleme Şablonu: Seç, Keşfet, Seçimi Geri Al” dersin tüm metni burada web'de ücretsiz olarak okunabilir. Etkileşimli olarak pratik yapmak (yerleşik kod editörü ve 7/24 yapay zeka koçu) ve Coding Interview Prep kursunun geri kalanını açmak için CoddyKit PRO'ya yükselt. Coding Interview Prep kursu toplamda 4 dersten oluşur.

“Geri İzleme Şablonu: Seç, Keşfet, Seçimi Geri Al” dersinde ne öğreneceğim?

Üç adımlı geri izleme iskeletini uygulayın, küçük bir örnek üzerinde izini sürün ve budama koşullarının nereye yerleştirileceğini belirleyin. Coding Interview Prep ile uygulamalı kodu tarayıcıda doğrudan çalıştırarak pratik yaparsın ve 7/24 yapay zeka koçu dersi çalışırken sorularını yanıtlar.

Coding Interview Prep öğrenmeye başlamak için deneyim gerekli mi?

Önceden deneyim gerekmez. CoddyKit'te Coding Interview Prep, başlangıçtan ileri seviyeye kadar yapılandırıldığı için buradan başlayabilir veya başından başlayıp kendi hızında ilerleme yapabilirsin. Bu, 4 dersinin 1. dersidir.

“Geri İzleme Şablonu: Seç, Keşfet, Seçimi Geri Al” dersi ne kadar sürer?

Çoğu CoddyKit dersi yaklaşık 5–10 dakika sürer. Her biri kısa ve etkileşimli olduğu için sabit ilerleme yaparsın ve web ile uygulama arasında tam olarak bıraktığın yerden devam edebilirsin.

Bu Coding Interview Prep dersinde kod yazıp çalıştırabilir miyim?

Evet. Her Coding Interview Prep dersi yerleşik bir kod editörü içerir, bu sayede tarayıcıda gerçek kod yazıp çalıştırabilir ve anlık yapay zeka geri bildirimi alırsın — yerel kurulum gerekli değildir.

Bu kursun tüm dersleri

  1. Geri İzleme Şablonu: Seç, Keşfet, Seçimi Geri Al
  2. Alt Kümeler ve Kuvvet Kümesi
  3. Permütasyonlar ve Kombinasyonlar
  4. N-Vezir ve Kısıt Yayılımı
← Coding Interview Prep Sayfasına Dön