0Pricing
DSA Interview Prep · Ders

Tekdüze Yığın: Artan ve Azalan

O(n) sürede sonraki daha büyük öğe ve önceki daha küçük öğe sorgularını verimli biçimde yanıtlamak için artan veya azalan bir yığın tutun.

Tekdüze Yığın: Artan ve Azalan, CoddyKit'te ücretsiz bir DSA 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, 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.

Monoton Yığın Nedir?

Monoton yığın, öğelerini sıralı bir düzende tutan bir yığındır (alttan üste doğru her zaman artan veya her zaman azalan). Yeni bir öğeyi yığına eklemeden önce, monoton değişmezi ihlal eden tüm öğeleri pop ederiz. Bu kısıtlı yapı, aksi durumda O(n²) iç içe döngüler gerektirecek problemlerin O(n) zamanda çözülmesini sağlar.

Temel fikir şudur: öğelerin her biri en fazla bir kez yığına eklenir ve bir kez pop edilir; bu nedenle dizinin tamamında gezinme boyunca toplam işlem sayısı O(n)'dir — O(n²) değil. Bir öğeyi pop ettiğimiz anda, beklediği yanıtı bulmuş oluruz.

# Monotonic increasing stack (bottom to top: smallest to largest)
stack = []
for val in [3, 1, 4, 1, 5, 9, 2, 6]:
    while stack and stack[-1] > val:
        stack.pop()          # maintain increasing invariant
    stack.append(val)
print('Increasing stack (left-to-right):', stack)  # [1, 1, 2, 6]

# Monotonic decreasing stack (bottom to top: largest to smallest)
stack = []
for val in [3, 1, 4, 1, 5, 9, 2, 6]:
    while stack and stack[-1] < val:
        stack.pop()          # maintain decreasing invariant
    stack.append(val)
print('Decreasing stack (left-to-right):', stack)  # [9, 6]

Bir Sonraki Daha Büyük Öğe I

Bir Sonraki Daha Büyük Öğe probleminde her öğe için sağındaki ilk daha büyük öğeyi bulmanız gerekir. Kaba kuvvet kullanan O(n²) çift döngü çok yavaştır. Azalan monoton yığın ile bu problemi O(n) zamanda çözeriz.

Öğeleri soldan sağa işleyin. i öğesini yığına eklemeden önce, yığındaki nums[i]'den küçük tüm öğeleri pop edin; nums[i], bunların tümü için bir sonraki daha büyük öğedir. Tüm öğeler işlendikten sonra yığında kalan öğelerin sağında daha büyük bir öğe yoktur (yanıt = -1).

def next_greater_element(nums):
    n = len(nums)
    result = [-1] * n
    stack = []   # stores indices; stack values are decreasing

    for i in range(n):
        # Pop elements smaller than nums[i]
        while stack and nums[stack[-1]] < nums[i]:
            idx = stack.pop()
            result[idx] = nums[i]   # nums[i] is next greater for idx
        stack.append(i)
    # Remaining elements in stack have no next greater => keep -1
    return result

nums = [2, 1, 2, 4, 3]
print(next_greater_element(nums))  # [4, 2, 4, -1, -1]

nums2 = [1, 3, 2, 4]
print(next_greater_element(nums2)) # [3, 4, 4, -1]

Sonraki Daha Büyük Eleman: Algoritmanın İzlenmesi

[2, 1, 2, 4, 3] dizisini adım adım izleyelim. Henüz sonraki daha büyük elemanı bulunmamış dizinlerden oluşan azalan bir yığın tutuyoruz.

  • i=0, val=2: yığın boş, 0'ı ekleyin. Yığın: [0]
  • i=1, val=1: 1 < nums[0]=2, 1'i ekleyin. Yığın: [0,1]
  • i=2, val=2: 1'i pop edin (nums[1]=1 < 2), result[1]=2; şimdi nums[0]=2, 2'den küçük değil, 2'yi ekleyin. Yığın: [0,2]
  • i=3, val=4: 2'yi pop edin (result[2]=4), 0'ı pop edin (result[0]=4), 3'ü ekleyin. Yığın: [3]
  • i=4, val=3: 3 < nums[3]=4, 4'ü ekleyin. Yığın: [3,4]
  • Son: yığın [3,4] için result=-1
def next_greater_trace(nums):
    n = len(nums)
    result = [-1] * n
    stack = []
    for i in range(n):
        print(f'i={i} val={nums[i]}: stack={[nums[s] for s in stack]}', end=' => ')
        while stack and nums[stack[-1]] < nums[i]:
            idx = stack.pop()
            result[idx] = nums[i]
            print(f'pop {nums[idx]}, NGE={nums[i]};', end=' ')
        stack.append(i)
        print(f'push {nums[i]}, stack={[nums[s] for s in stack]}')
    print('Result:', result)
    return result

next_greater_trace([2, 1, 2, 4, 3])

Önceki Daha Küçük Eleman

Monotonik yığınlar ayrıca önceki daha küçük eleman (PSE) sorgularını da yanıtlar: her eleman için, solunda bulunan ve ondan küçük olan en yakın elemanı bulur. Daha büyük bir elemanda pop yapmak yerine, daha büyük veya eşit bir elemanda pop yapar ve eklemeden önce yığının tepesini PSE olarak kaydederiz.

Yön değişir: elemanları yine soldan sağa işleriz; ancak soruları pop işlemi sırasında yanıtlamak yerine, eklemeden hemen önce yanıtlarız. O andaki yığın tepesi, soldaki en yakın küçük elemandır. Yığın boşsa, solda daha küçük bir eleman yoktur (yanıt = -1 veya bir nöbetçi değer).

def previous_smaller_element(nums):
    n = len(nums)
    result = [-1] * n
    stack = []   # monotonic increasing (values increase bottom to top)

    for i in range(n):
        # Pop elements >= current (maintain strictly increasing invariant)
        while stack and nums[stack[-1]] >= nums[i]:
            stack.pop()
        # Top of stack is previous smaller element (if exists)
        if stack:
            result[i] = nums[stack[-1]]
        stack.append(i)
    return result

nums = [4, 5, 2, 10, 8]
print('PSE:', previous_smaller_element(nums))  # [-1, 4, -1, 2, 2]

nums2 = [1, 3, 2, 5, 4]
print('PSE:', previous_smaller_element(nums2)) # [-1, 1, 1, 2, 2]

Günlük Sıcaklıklar: Daha Sıcak Günleri Beklemek

Günlük Sıcaklıklar problemi (LeetCode 739): günlük sıcaklıklar verildiğinde, her elemanın daha sıcak bir sıcaklığa ulaşılmasına kaç gün kaldığını gösteren bir dizi döndürün. Bu, sonraki daha büyük eleman örüntüsünün aynısıdır; ancak daha büyük değeri değil, gün sayısını (dizin farkını) isteriz.

Dizinlerden oluşan monotonik azalan bir yığın kullanın. temps[j] < temps[i] koşulunu sağlayan tüm j dizinlerini yığından pop edin ve result[j] = i - j olarak ayarlayın. Kalan dizinlerin gelecekte daha sıcak bir günü yoktur (sonuç = 0).

def daily_temperatures(temperatures):
    n = len(temperatures)
    result = [0] * n
    stack = []   # indices of unresolved days

    for i in range(n):
        while stack and temperatures[stack[-1]] < temperatures[i]:
            j = stack.pop()
            result[j] = i - j   # days until warmer
        stack.append(i)
    return result

temps = [73, 74, 75, 71, 69, 72, 76, 73]
print(daily_temperatures(temps))  # [1, 1, 4, 2, 1, 1, 0, 0]

temps2 = [30, 40, 50, 60]
print(daily_temperatures(temps2)) # [1, 1, 1, 0]  (always warmer next day)

temps3 = [30, 60, 90]
print(daily_temperatures(temps3)) # [1, 1, 0]

Artan ve Azalan Yığın: Hangisi Ne Zaman Kullanılır

Doğru yığın yönünü seçmek çok önemlidir:

  • Monotonik azalan yığın (mevcut değer > tepe olduğunda pop yapar): sonraki daha büyük eleman ve önceki daha büyük eleman sorgularını yanıtlar. Günlük sıcaklıklar, en büyük dikdörtgen ve yağmur suyu biriktirme problemlerinde kullanılır.
  • Monotonik artan yığın (mevcut değer < tepe olduğunda pop yapar): sonraki daha küçük eleman ve önceki daha küçük eleman sorgularını yanıtlar. Hisse senedi fiyatlarının aralığını bulmada ve bir sıradaki görünür kişi sayısını hesaplamada kullanılır.

Unutmayın: pop işlemine neden olan eleman, pop edilen elemanın sorgusuna verilen yanıttır — koruduğunuz değişmeze bağlı olarak sonraki daha büyük veya sonraki daha küçük eleman.

# Summary: which stack type for which query?
queries = {
    'Next Greater Element':    'Decreasing stack (pop when new > top)',
    'Next Smaller Element':    'Increasing stack (pop when new < top)',
    'Previous Greater Element': 'Decreasing stack (answer = top before push)',
    'Previous Smaller Element': 'Increasing stack (answer = top before push)',
}
for query, approach in queries.items():
    print(f'{query}:\n  => {approach}\n')

# Mnemonic:
# NGE/PGE => decreasing stack (we pop smaller elements, finding their next/prev larger)
# NSE/PSE => increasing stack (we pop larger elements, finding their next/prev smaller)

Dairesel Sonraki Daha Büyük Eleman

Sonraki Daha Büyük Eleman II (LeetCode 503): dairesel bir dizi (başa sarma) verildiğinde, sonraki daha büyük elemanı bulun. Buradaki püf noktası, dizinleri iki tur kullanarak diziyi iki kez işlemektir: 0'dan 2n-1'e kadar ilerleyin ve başa sarmak için index % n kullanın. İki kez saymayı önlemek için yalnızca 0 ile n-1 arasındaki dizinleri (ilk turda) ekleyin.

Başka bir seçenek olarak, ikinci turda yeni dizinler eklemeden yalnızca pop işlemi yaparak diziyi işleyebilirsiniz. Bu yöntem, diziyi gerçekten çoğaltmadan dairesel olarak ileriyi doğru biçimde görmenizi sağlar ve bellek kullanımını O(n) düzeyinde tutar.

def next_greater_element_circular(nums):
    n = len(nums)
    result = [-1] * n
    stack = []

    for i in range(2 * n):
        while stack and nums[stack[-1]] < nums[i % n]:
            idx = stack.pop()
            result[idx] = nums[i % n]
        if i < n:
            stack.append(i)   # only push real indices (0..n-1)
    return result

print(next_greater_element_circular([1, 2, 1]))    # [2, -1, 2]
print(next_greater_element_circular([1, 2, 3, 4, 3]))  # [2, 3, 4, -1, 4]
print(next_greater_element_circular([5, 4, 3, 2, 1]))  # [-1, 5, 5, 5, 5]

Hisse Senedi Aralığı Problemi

Hisse Senedi Aralığı problemi: günlük hisse senedi fiyatları verildiğinde, her günün aralığını, yani fiyatı bugünün fiyatından küçük veya ona eşit olan ardışık önceki günlerin sayısını hesaplayın. Bu, kılık değiştirmiş bir önceki daha büyük eleman problemidir: aralık, bugünden geriye doğru kesinlikle daha yüksek fiyata sahip en yakın güne olan uzaklıktır.

Monotonik azalan bir yığın kullanın. i. günü işlerken, fiyatı mevcut fiyattan küçük veya ona eşit olan tüm günleri pop edin. Yığın boş değilse aralık i - stack[-1], boşsa aralık i + 1 olur (fiyat şimdiye kadarki en yüksek fiyattır). Ardından i'yi ekleyin.

def stock_span(prices):
    spans = []
    stack = []   # indices of prices forming decreasing sequence

    for i, price in enumerate(prices):
        while stack and prices[stack[-1]] <= price:
            stack.pop()
        span = i - stack[-1] if stack else i + 1
        spans.append(span)
        stack.append(i)
    return spans

prices = [100, 80, 60, 70, 60, 75, 85]
print('Prices:', prices)
print('Spans: ', stock_span(prices))  # [1, 1, 1, 2, 1, 4, 6]

# Verification for day 5 (price=75): prev higher is day 1 (80), span = 5-1 = 4
# Day 6 (price=85): prev higher is day 0 (100), span = 6-0 = 6

Sıradaki Görünür Kişiler İçin Monotonik Yığın

Bir Sıradaki Görünür Kişi Sayısı problemi: İnsanlar, her birinin farklı bir boya sahip olduğu bir sırada durur. Aralarındaki tüm kişilerin boyu her ikisinden de kısa olduğunda, i. kişi j. kişiyi görebilir (j > i). Bu problemde monotonik azalan bir yığın kullanılır.

Sağdan sola işleyin. Boylardan oluşan azalan bir yığın tutun. Her kişi için görebildiği kişi sayısını hesaplayın: boyu daha kısa olan herkesi pop edin (görünürler, ancak sonrasındaki kişileri engellerler); pop işleminden sonra yığın boş değilse 1 ekleyin (ilk daha uzun kişi de görünürdür). Her kişi en fazla bir kez eklenip pop edildiğinden toplam süre O(n) olur.

def visible_people(heights):
    n = len(heights)
    result = [0] * n
    stack = []   # decreasing monotonic stack (heights)

    for i in range(n - 1, -1, -1):   # right to left
        count = 0
        while stack and stack[-1] < heights[i]:
            stack.pop()
            count += 1   # can see this shorter person
        if stack:
            count += 1   # can see the first person >= heights[i]
        result[i] = count
        stack.append(heights[i])
    return result

heights = [10, 6, 8, 5, 11, 9]
print('Heights:', heights)
print('Visible:', visible_people(heights))  # [3, 1, 2, 1, 1, 0]

O(n) Garantisi: Her Eleman Neden En Fazla Bir Kez Eklenir ve Pop Edilir

Monotonik yığın algoritmalarının O(n) zaman garantisi basit bir amortisman argümanından kaynaklanır: her eleman yığına tam bir kez eklenir ve en fazla bir kez pop edilir. Hiçbir eleman birden fazla kez eklenemez veya pop edilemez. Bu nedenle, iç içe while döngüsü O(n²) izlenimi verse de, döngünün tamamındaki ekleme + pop işlemlerinin toplam sayısı en fazla 2n olur ve toplam iş O(n) düzeyindedir.

Bu amortisman analizini mülakatlarda açıkça ifade etmek önemlidir. While döngüsü her yinelemede n kez çalışmaz; yalnızca bekleyen elemanları pop edecek kadar çalışır ve bu elemanlar pop edildikten sonra kalıcı olarak ortadan kalkar.

def next_greater_instrumented(nums):
    result = [-1] * len(nums)
    stack = []
    pushes = pops = 0

    for i in range(len(nums)):
        while stack and nums[stack[-1]] < nums[i]:
            idx = stack.pop()
            result[idx] = nums[i]
            pops += 1
        stack.append(i)
        pushes += 1

    print(f'n={len(nums)}, pushes={pushes}, pops={pops}')
    print(f'Total operations = {pushes + pops} <= 2n = {2*len(nums)}')
    return result

import random
nums = random.sample(range(1000), 100)
next_greater_instrumented(nums)
# Confirm: total operations always <= 2n

Monotonik Yığın Problemlerini Tanıma

Bir problem en yakın daha büyük/küçük elemanı, fiyat aralığını, bir sıradaki görünür elemanları veya histogram tabanlı alanları soruyorsa büyük olasılıkla monotonik bir yığın gerektirir. Şu anahtar kelimeleri ve örüntüleri arayın: her eleman, bir yöndeki en yakın ilgili elemandan gelen yanıta ihtiyaç duyar (sol veya sağ).

Kaba kuvvet çözümü her elemandan başlayarak sola veya sağa tarama yapıyorsa (O(n²)), bu taramayı monotonik bir yığınla değiştirin. Yığın aday yanıtları 'hatırlar', ilgisiz olanları eler ve doğru yanıtı tam gerektiği anda pop eder.

# Monotonic stack problem recognition guide
patterns = [
    ('Next/previous greater element', 'Decreasing stack; answer found on pop'),
    ('Next/previous smaller element', 'Increasing stack; answer found on pop'),
    ('Days until warmer/colder',       'Stack of indices; answer = i - j'),
    ('Stock span',                     'Decreasing stack; span = i - prev larger idx'),
    ('Largest rectangle in histogram', 'Increasing stack; area computed on pop'),
    ('Trapping rain water',            'Decreasing stack or two-pointer'),
    ('Sliding window maximum',         'Decreasing deque of indices'),
]
print('Monotonic Stack / Deque Pattern Guide:')
print('='*60)
for problem, approach in patterns:
    print(f'Problem: {problem}')
    print(f'  Approach: {approach}')
    print()

Hızlı Kontrol

Bu derste öğretilen Veri Yapıları ve Algoritmalar — Kodlama Mülakatına Hazırlık kavramlarını anlayışınızı sınayın.

Ders Özeti

Bu derste şunları öğrendiniz: monotonik bir yığın, eklemeden önce değişmezi ihlal eden elemanları pop ederek artan veya azalan sırayı korur, azalan yığın sonraki/önceki daha büyük elemanı, artan yığın ise sonraki/önceki daha küçük elemanı bulur ve her eleman en fazla bir kez eklenip pop edildiğinden toplam süre O(n)'dir — O(n²) değil. Sırada, histogramdaki en büyük dikdörtgeni bulmak için monotonik yığını uygulayacağız.

Sıkça Sorulan Sorular

“Tekdüze Yığın: Artan ve Azalan” dersi ücretsiz mi?

Evet — “Tekdüze Yığın: Artan ve Azalan” 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.

“Tekdüze Yığın: Artan ve Azalan” dersinde ne öğreneceğim?

O(n) sürede sonraki daha büyük öğe ve önceki daha küçük öğe sorgularını verimli biçimde yanıtlamak için artan veya azalan bir yığın tutun. 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 1. dersidir.

“Tekdüze Yığın: Artan ve Azalan” 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. Tekdüze Yığın: Artan ve Azalan
  2. Histogramdaki En Büyük Dikdörtgen
  3. Tekdüze Kuyrukla Kayan Pencere Maksimumu
  4. Yağmur Suyu Biriktirme: Yığın ve İki İşaretçi
← DSA Interview Prep Sayfasına Dön