En Uzun Ardışık Dizi ve LRU Önbelleği
Küme kullanarak en uzun ardışık dizi problemini O(n) sürede çözün, ardından OrderedDict kullanarak bir LRU önbelleği tasarlayın.
En Uzun Ardışık Dizi ve LRU Önbelleği, CoddyKit'te ücretsiz bir Coding Interview Prep dersidir. Bu, 4 dersinin 4. 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.
En Uzun Ardışık Dizi Problemi
LeetCode 128 En Uzun Ardışık Dizi: sıralanmamış bir dizi verildiğinde, ardışık tamsayılardan oluşan en uzun dizinin uzunluğunu bulun. Örnek: [100,4,200,1,3,2], uzunluğu 4 olan [1,2,3,4] ardışık dizisini içerir. Zorluk, sıralayıp taramanın sağlayacağı O(n log n) yerine problemi O(n) sürede çözmektir.
Temel fikir şudur: O(1) üyelik denetimleri için bir küme kullanın ve bir diziyi yalnızca en küçük öğesinden başlayarak sayın; bunu, önceki öğenin kümede bulunmadığını kontrol ederek belirlersiniz.
def longestConsecutive(nums):
num_set = set(nums)
best = 0
for n in num_set:
if n - 1 not in num_set: # n is the start of a sequence
curr_n = n
length = 1
while curr_n + 1 in num_set:
curr_n += 1
length += 1
best = max(best, length)
return best
print(longestConsecutive([100,4,200,1,3,2])) # 4
print(longestConsecutive([0,3,7,2,5,8,4,6,0,1])) # 9O(n) Kanıtı Neden Geçerlidir
Her sayı, dıştaki for döngüsünün tüm yinelemeleri boyunca while döngüsünde en fazla bir kez ziyaret edilir. Bir for döngüsünün içinde while döngüsü bulunmasına rağmen, tüm dış yinelemelerdeki while döngüsü yinelemelerinin toplam sayısı en fazla n'dir; çünkü her sayı en fazla bir dizinin sonraki öğesi olabilir. Bu amortisman analizi, monoton yığın analizine benzer şekilde genel olarak O(n) sonucunu verir.
# Demonstrate O(n) total inner iterations
nums = list(range(1000)) # worst case: one long sequence
num_set = set(nums)
inner_iters = 0
for n in num_set:
if n - 1 not in num_set:
curr = n
while curr + 1 in num_set:
curr += 1
inner_iters += 1
print('n =', len(nums), ' total inner iterations =', inner_iters)
# inner_iters = n-1 <= n => O(n)Alternatif: Sıralama Tabanlı Yaklaşım
Karşılaştırma için sıralayıp tarama yaklaşımı O(n log n) sürede çalışır: diziyi sıralayın, ardışık tekrarları tekilleştirin, ardından ardışık serileri sayın. Daha yavaş olsa da sıralama yerinde yapılırsa O(1) ek alan kullanır. Küme yaklaşımı O(n) ek alan kullanır. Bir mülakatta her iki yaklaşımı da belirtin ve alan kısıtları göz önüne alındığında O(n log n) çözümünün kabul edilebilir olup olmadığını açıklayın.
def longestConsecutive_sort(nums):
if not nums:
return 0
nums.sort()
best = length = 1
for i in range(1, len(nums)):
if nums[i] == nums[i-1]:
continue # skip duplicates
if nums[i] == nums[i-1] + 1:
length += 1
best = max(best, length)
else:
length = 1
return best
print(longestConsecutive_sort([100,4,200,1,3,2])) # 4LRU Önbelleği Nedir?
LRU (En Son Kısa Süre Önce Kullanılan) önbelleği, sabit kapasiteli bir veri yapısıdır; dolu olduğunda ve yeni bir öğe eklenmesi gerektiğinde en uzun süredir kullanılmayan öğeyi çıkarır. İşlemler: get(key), anahtar varsa değeri döndürür ve onu yakın zamanda kullanılmış olarak işaretler; anahtar yoksa -1 döndürür. put(key, value), çifti ekler ve kapasite doluysa LRU öğesini çıkarır.
LRU önbellekleri işletim sistemlerinde (sayfa değiştirme), tarayıcı önbelleklerinde ve veritabanı sorgu önbelleklerinde kullanılır. LeetCode 146, bir önbelleği get ve put işlemleri O(1) olacak şekilde uygulamanızı ister.
OrderedDict Kullanarak LRU Önbelleği
Python'un collections.OrderedDict yapısı ekleme sırasını korur ve bir öğeyi en son kullanılan olarak işaretlemek için move_to_end(key) (O(1)) desteği sunar. put işleminde anahtarı sona taşıyın; kapasite aşılırsa ilk öğeyi çıkarın (LRU). Bu yaklaşım, dahili olarak çift bağlı liste ve karma tablo tarafından desteklenen yerleşik bir yapı kullanarak get ve put işlemlerini O(1) sürede gerçekleştirir.
from collections import OrderedDict
class LRUCache:
def __init__(self, capacity):
self.capacity = capacity
self.cache = OrderedDict()
def get(self, key):
if key not in self.cache:
return -1
self.cache.move_to_end(key) # mark as recently used
return self.cache[key]
def put(self, key, value):
if key in self.cache:
self.cache.move_to_end(key)
self.cache[key] = value
if len(self.cache) > self.capacity:
self.cache.popitem(last=False) # evict LRU (first item)
cache = LRUCache(2)
cache.put(1, 1); cache.put(2, 2)
print(cache.get(1)) # 1 (and 1 becomes most recently used)
cache.put(3, 3) # evict key 2 (LRU)
print(cache.get(2)) # -1
cache.put(4, 4) # evict key 1 (LRU)
print(cache.get(1)) # -1
print(cache.get(3)) # 3
print(cache.get(4)) # 4Sıfırdan LRU Önbelleği: Çift Bağlı Liste + HashMap
Sıfırdan uygulama, çift bağlı liste (düğümün O(1) sürede silinmesini sağlamak için) ve karma tablo (anahtara göre düğümü O(1) sürede bulmak için) kullanır. Liste, sıralamayı LRU'dan (head.next) MRU'ya (tail.prev) kadar korur. Sahte baş ve kuyruk bekçileri, sınır noktalarında ekleme ve silme için gereken özel durumları ortadan kaldırır.
class DNode:
def __init__(self, key=0, val=0):
self.key = key
self.val = val
self.prev = None
self.next = None
class LRUCacheDLL:
def __init__(self, capacity):
self.cap = capacity
self.map = {} # key -> DNode
self.head = DNode() # dummy LRU end
self.tail = DNode() # dummy MRU end
self.head.next = self.tail
self.tail.prev = self.head
def _remove(self, node):
node.prev.next = node.next
node.next.prev = node.prev
def _add_to_tail(self, node):
node.prev = self.tail.prev
node.next = self.tail
self.tail.prev.next = node
self.tail.prev = node
def get(self, key):
if key not in self.map:
return -1
node = self.map[key]
self._remove(node)
self._add_to_tail(node)
return node.val
def put(self, key, val):
if key in self.map:
self._remove(self.map[key])
node = DNode(key, val)
self._add_to_tail(node)
self.map[key] = node
if len(self.map) > self.cap:
lru = self.head.next
self._remove(lru)
del self.map[lru.key]
cache = LRUCacheDLL(2)
cache.put(1,1); cache.put(2,2)
print(cache.get(1)) # 1
cache.put(3,3)
print(cache.get(2)) # -1 (evicted)LRU İçin Neden Çift Bağlı Liste Kullanılır?
Tek bağlı liste, önceki düğüm bilinmeden rastgele bir düğümü O(1) sürede silemez. Çift bağlı liste hem prev hem de next işaretçilerini saklar; böylece düğüm başvurusu verildiğinde silme O(1) sürede yapılır. Karma tablo, anahtara göre düğüme O(1) sürede erişim sağlar. Birlikte kullanıldıklarında: get(anahtar), düğümü bulmak ve kuyruğa taşımak için O(1) sürer; put(anahtar), ekleme için O(1) ve LRU düğümünü baştan silmek için O(1) sürer.
# Why not a singly linked list?
# To remove a node you need its predecessor
# With SLL: must traverse from head to find predecessor => O(n)
# With DLL: node.prev IS the predecessor => O(1) removal
print('SLL removal: O(n) — must find predecessor by traversal')
print('DLL removal: O(1) — node.prev is immediately available')
print('Hash map lookup: O(1) — get DNode reference by key')
print('Combined LRU get/put: O(1) average')LFU Önbelleği (En Az Sıklıkla Kullanılan)
Daha zor bir varyant olan LFU önbelleğinde (LeetCode 460), erişim sayısı en düşük olan öğe çıkarılır. Eşitlikler, kullanım yakınlığına göre bozulur: en az kullanılanlar arasından en uzun süredir kullanılmayan öğe seçilir. Uygulama üç veri yapısı gerektirir: anahtardan değere eşleme, anahtardan frekansa eşleme ve her frekans grubu içindeki ekleme sırasını korumak için frekanstan OrderedDict'e eşleme. LFU get ve put işlemleri amortismanlı olarak O(1) sürede çalışır.
from collections import defaultdict, OrderedDict
class LFUCache:
def __init__(self, capacity):
self.cap = capacity
self.min_f = 0
self.kv = {} # key -> val
self.kf = {} # key -> freq
self.fk = defaultdict(OrderedDict) # freq -> {key: None}
def _touch(self, key):
f = self.kf[key]
self.kf[key] = f + 1
del self.fk[f][key]
if not self.fk[f] and f == self.min_f:
self.min_f += 1
self.fk[f+1][key] = None
def get(self, key):
if key not in self.kv:
return -1
self._touch(key)
return self.kv[key]
def put(self, key, val):
if self.cap == 0: return
if key in self.kv:
self.kv[key] = val
self._touch(key)
else:
if len(self.kv) == self.cap:
lfu_key, _ = self.fk[self.min_f].popitem(last=False)
del self.kv[lfu_key]; del self.kf[lfu_key]
self.kv[key] = val; self.kf[key] = 1
self.fk[1][key] = None; self.min_f = 1Tasarım Örüntüleri: Karma Tablo + Bağlı Liste
LRU önbelleği güçlü bir tasarım örüntüsünü gösterir: anahtar aramasını O(1) sürede yapmak için karma tabloyu, sıralı işlemleri O(1) sürede yapmak için bağlı listeyle birleştirin. Bu örüntü çeşitli mülakat tasarımı problemlerinde görülür: LRU önbelleği, LFU önbelleği, atlama listeleri ve bazı kuyruk varyantları. Bir problem hem O(1) arama hem de O(1) sıra tabanlı işlemler gerektirdiğinde bu birleşimi değerlendirin.
Mülakatlarda bu örüntüyü açıkça ifade etmek, sistem düzeyinde düşünme becerinizi ve klasik veri yapısı birleşimlerine aşinalığınızı gösterir.
Matriste Ardışık Dizi
Ardışık dizi fikrinin iki boyutlu bir genişletmesi: tamsayılardan oluşan bir matris verildiğinde, izlenebilen en uzun ardışık dizinin uzunluğunu bulun; her adımda bitişik bir hücreye geçilir. Bu yaklaşım BFS/DFS ile ardışık dizi için küme yaklaşımını birleştirir. Her değerin konumunu saklayın, ardından her başlangıç değeri için değer + 1'in komşu olarak bulunup bulunmadığını kontrol edin.
# Simpler: find longest consecutive values in a 2D matrix (no adjacency)
def longestConsecutiveMatrix(matrix):
all_vals = set()
for row in matrix:
for v in row:
all_vals.add(v)
best = 0
for v in all_vals:
if v - 1 not in all_vals: # start of sequence
length = 0
while v in all_vals:
v += 1
length += 1
best = max(best, length)
return best
m = [[1, 5, 3], [4, 6, 2], [8, 7, 9]]
print(longestConsecutiveMatrix(m)) # 9 (1..9 all present)Mülakat Özeti: Küme + HashMap Gücü
Bu iki problem ortak bir temayı paylaşır: doğru karma yapıyı kullanarak O(n log n) veya O(n²) problemlerini O(n)'e dönüştürmek. En uzun ardışık dizi, önceki öğenin mevcut olup olmadığını O(1) sürede yanıtlamak için küme kullanır. LRU önbelleği, düğümü anında bulmak için karma tablo ve sırayı O(1) sürede güncellemek için çift bağlı liste kullanır. Her ikisi de yavaş dolaşma işlemlerinin yerine O(1) üyelik veya arama işlemleri koyar.
Bir mülakatçı O(n log n)'den daha iyisini yapabilir misiniz? diye sorduğunda yanıt neredeyse her zaman sıralamadan kaçınmak için karma tablo veya karma küme kullanmaktır.
Hızlı Kontrol
Bu dersteki Veri Yapıları ve Algoritmalar — Kodlama Mülakatı Hazırlığı kavramlarını ne kadar anladığınızı sınayın.
Ders Özeti
Bu derste şunları öğrendiniz: en uzun ardışık dizi, üyelik için O(1) sürede çalışan bir küme kullanarak ve sayımı yalnızca dizilerin başlangıçlarından başlatarak O(n) sürede bulunur, LRU önbelleği OrderedDict (veya sıfırdan oluşturulmuş karma tablo ve çift bağlı liste) kullanarak get ve put işlemlerini O(1) sürede gerçekleştirir ve karma tablo ile bağlı liste örüntüsü, sıraya duyarlı O(1) veri yapıları için yeniden kullanılabilir bir yapı taşıdır. Sırada temel durum, güven ve oluşturma çerçevesiyle özyinelemeyi yeniden ele alacağız.
Sıkça Sorulan Sorular
“En Uzun Ardışık Dizi ve LRU Önbelleği” dersi ücretsiz mi?
Evet — “En Uzun Ardışık Dizi ve LRU Önbelleği” 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.
“En Uzun Ardışık Dizi ve LRU Önbelleği” dersinde ne öğreneceğim?
Küme kullanarak en uzun ardışık dizi problemini O(n) sürede çözün, ardından OrderedDict kullanarak bir LRU önbelleği tasarlayın. 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 4. dersidir.
“En Uzun Ardışık Dizi ve LRU Önbelleği” 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
- Karma İşlevlerinin İç Yapısı ve Çakışma Yönetimi
- İki Toplam ve Çeşitli Türevleri
- Sıklık Sayma ve Gruplama
- En Uzun Ardışık Dizi ve LRU Önbelleği