0Pricing
DSA Interview Prep · Ders

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 DSA 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, DSA Interview Prep öğrenme yolunun bir parçasıdır ve ilerlemeniz web ve CoddyKit uygulaması arasında senkronize olur. DSA 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]))  # 9

O(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]))  # 4

LRU Ö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))  # 4

Sı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 = 1

Tasarı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 DSA Interview Prep kursunun geri kalanını açmak için CoddyKit PRO'ya yükselt. DSA 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. DSA 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.

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

Önceden deneyim gerekmez. CoddyKit'te DSA 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 DSA Interview Prep dersinde kod yazıp çalıştırabilir miyim?

Evet. Her DSA 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. Karma İşlevlerinin İç Yapısı ve Çakışma Yönetimi
  2. İki Toplam ve Çeşitli Türevleri
  3. Sıklık Sayma ve Gruplama
  4. En Uzun Ardışık Dizi ve LRU Önbelleği
← DSA Interview Prep Sayfasına Dön