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 TrueEn 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')) # TrueKarar 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), pathGeri İ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 TrueHı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
- Geri İzleme Şablonu: Seç, Keşfet, Seçimi Geri Al
- Alt Kümeler ve Kuvvet Kümesi
- Permütasyonlar ve Kombinasyonlar
- N-Vezir ve Kısıt Yayılımı