Bölüm 7: Yığınlar

Veri Yapıları
LIFO ve temel işlemlerDizi ile yığınBağlı liste ile yığın Parantez kontrolüSonraki büyük öğeHanoi kuleleri Stock spanInfix → postfix / prefixPostfix değerlendirme
Sercan KÜLCÜ · Giresun Üniversitesi · Bilgisayar Mühendisliği

Giriş

Öğrenme Çıktıları

LIFO modeli

Yığının son giren ilk çıkar davranışını açıklamak.

Gerçekleme

Yığını dizi ve bağlı liste kullanarak gerçekleştirmek.

Uygulamalar

Yığınları ifade çözümleme ve çağrı yönetiminde kullanmak.

Maliyet analizi

Temel yığın işlemlerinin zaman ve alan maliyetlerini değerlendirmek.

Yığın ADT

Yığın (Stack)

  • Ekleme ve çıkarma yalnızca bir uçtan, yığının üstünden (top) yapılır.
  • Üst üste konmuş tabaklar ya da kutular gibi düşünülebilir.
  • En alttaki öğeye ulaşmak için üstündeki öğeler tek tek çıkarılmalıdır.
  • LIFO (Last-In, First-Out · son giren ilk çıkar) ya da eşdeğeri FILO (ilk giren son çıkar) mantığıyla çalışır.

Yeni tabak en üste konur, alınan tabak da en üstten alınır.

Geri al (undo)Son yapılan işlem ilk geri alınır.
Tarayıcı “geri”En son ziyaret edilen sayfaya dönülür.
Fonksiyon çağrılarıEn son çağrılan fonksiyon ilk biter.

Yığın ADT

Temel İşlemler

İşlemAçıklama
push(x)Üste yeni öğe x ekler.
pop()Üstteki öğeyi çıkarır ve döndürür.
top()Üstteki öğeyi çıkarmadan döndürür (Java: peek()).
isEmpty()Yığın boş mu?
size()Öğe sayısını döndürür.
Kullanıcı yalnızca üstteki öğeyi görür; ortadaki öğelere doğrudan erişim yoktur.

Yığın ADT

push ve pop Adım Adım

yığınboşİşlemDönen değersize

Başlangıçta yığın boş: size = 0, üstte öğe yok.

10yığın← topİşlemDönen değersizepush(10)—1

push(10): 10 boş yığına eklenir; üst (top) artık 10.

1015yığın← topİşlemDönen değersizepush(10)—1push(15)—2

push(15): 15, 10'un üstüne konur.

101520yığın← topİşlemDönen değersizepush(10)—1push(15)—2push(20)—3

push(20): 20 en üste konur; size = 3.

101520yığın← topİşlemDönen değersizepush(10)—1push(15)—2push(20)—3top()203

top(): üstteki 20 okunur ama çıkarılmaz.

1015yığın← top20← çıkanİşlemDönen değersizepush(10)—1push(15)—2push(20)—3top()203pop()202

pop(): son giren 20 ilk çıkar (LIFO).

10yığın← top15← çıkanİşlemDönen değersizepush(10)—1push(15)—2push(20)—3top()203pop()202pop()151

pop(): 15 çıkar; üst artık 10.

yığınboş10← çıkanİşlemDönen değersizepush(10)—1push(15)—2push(20)—3top()203pop()202pop()151pop()100

pop(): 10 çıkar; yığın yeniden boş.

yığınboşalt taşma!İşlemDönen değersizepush(10)—1push(15)—2push(20)—3top()203pop()202pop()151pop()100pop()hata!0

Boş yığında pop() → alt taşma (underflow) hatası.

Yığın ADT

Yığın ADT'sinin Java Arayüzü

public interface Stack<E> {
  int size();
  boolean isEmpty();
  void push(E e);
  E top();    // boşsa null
  E pop();    // boşsa istisna
}
  • ADT = ne? Arayüz yalnızca işlemleri ve anlamlarını söyler; verinin nasıl saklandığını söylemez.
  • Gerçekleme = nasıl? DiziYigin<E> implements Stack<E> ya da bağlı liste tabanlı bir sınıf.
  • Kod arayüze göre yazılırsa gerçekleme, çağıran kod değişmeden değiştirilebilir.
Boş yığındapop()top() / peek()
Kendi Stack<E>'imiz (yukarıdaki)istisna fırlatırnull döner
java.util.StackEmptyStackExceptionEmptyStackException
java.util.ArrayDequeNoSuchElementExceptionnull döner

Aynı soyut işlem kütüphaneye göre farklı “boş” davranışı gösterir: önce isEmpty() ile kontrol edin. Kendi arayüzümüz java.util.Stack ile aynı adı taşır; ikisini aynı dosyada import etmeyin.

Yığın ADT

İşlemlerin Algoritmaları (Dizi ile)

Yığın bir dizide tutulur; top üstteki öğenin indisidir ve boş yığında top = −1.

push(x)
başla
  eğer yığın doluysa   // top = kap−1
    hata ver: taşma (overflow)
  değilse
    top değerini 1 artır
    yığın[top] ← x
son
pop()
başla
  eğer yığın boşsa     // top = −1
    hata ver: alt taşma (underflow)
  değilse
    x ← yığın[top]; top 1 azalt
    x değerini döndür
son
top()
başla
  eğer yığın boşsa null döndür
  değilse yığın[top] döndür
son
isEmpty() · size()
isEmpty: top = −1 ise true, değilse false
size:    top + 1 döndür

Yığın ADT

Zaman ve Alan Karmaşıklığı

İşlemZamanNeden?
pushO(1)yalnızca üst değişir
popO(1)yalnızca üst değişir
topO(1)üst doğrudan bilinir
isEmpty, sizeO(1)sayaç / indis tutulur
bir öğeyi aramaO(n)öğeler tek tek çıkarılmalı
  • Temel işlemlerin süresi yığındaki öğe sayısından bağımsızdır.
  • n öğeli yığın O(n) bellek kullanır; işlemler ek bellek gerektirmez (O(1)).
  • Dinamik dizide kapasite dolunca dizi büyütülür: push amortize O(1).
Yığın, erişimi kısıtlayarak hız kazanır: “yalnızca üst” kuralı sayesinde hiçbir temel işlem öğeleri kaydırmaz ya da taramaz.

Gerçekleme

Yığın Türleri

Sabit boyutlu (kapasite 4)01012023034050push(50) → taşma (overflow) hatasıDinamik boyutlu (dolunca 2 katı)010120230340doluyeni dizi + kopyala1020304050
Sabit Boyutlu Yığın (Fixed Size Stack)
  • Kapasitesi baştan belirlenir; büyüyemez, küçülemez.
  • Dolu yığına ekleme → taşma (overflow).
  • Boş yığından çıkarma → alt taşma (underflow).
  • Arka planda sabit boyutlu bir dizi kullanır.
Dinamik Boyutlu Yığın (Dynamic Size Stack)
  • Çalışma sırasında büyüyüp küçülebilir.
  • Dolunca kapasitesini otomatik artırır (ör. iki katına çıkarır).
  • Öğe sayısı kapasiteye göre çok azalınca küçülebilir.
  • Dinamik dizi ya da bağlı liste ile gerçekleştirilir.

Boş yığından çıkarma (alt taşma) her iki türde de hatadır; taşma yalnızca sabit boyutlu yığında görülür.

Gerçekleme

Yığın Gerçekleme Yöntemleri

Yığın ADT'si bir dizi (array) ya da bağlı liste (linked list) ile gerçekleştirilebilir.

10015120234top = 2dizi: push → top++ , pop → top−−201510nulltopbağlı liste: push/pop listenin başında
Dizi Tabanlı push: top indisi artırılır, öğe dizi[top]'a yazılır.
pop: dizi[top] alınır, sonra top azaltılır.
Bağlı Liste Tabanlı push: yeni düğüm listenin başına eklenir.
pop: baştaki düğüm çıkarılır, değeri döndürülür.

Gerçekleme

Dizi Tabanlı Yığın

Bu yaklaşıma sınırlı yığın (bounded stack) da denir: kapasite oluşturulurken belirlenir ve değişmez.

05182334567veritop = 2kullanılmayan kapasite (null)size = top + 1 = 3

Avantajları

  • Basit ve kolay uygulanır.
  • Düğüm başına referans tutulmaz → bellek tasarrufu.
  • Bellek kullanımı sıkı biçimde kontrol edilir; belleği doldurmaya yönelik saldırılara karşı daha güvenlidir.
  • Öğeler ardışık → önbellek (cache) dostu.

Dikkat edilecekler

  • Eklemeden önce yığının dolu olmadığı kontrol edilmelidir.
  • Kapasite az seçilirse taşma, çok seçilirse boşa bellek.
  • Nesne tutan dizide çıkarılan hücreye null atanmalıdır; yoksa nesne bellekte gereksiz yere kalır.

Gerçekleme

Dizi Tabanlı Yığın: Java

public class DiziYigin<E> implements Stack<E> {
    private E[] veri;
    private int top = -1;                    // -1 → boş yığın
    public DiziYigin(int kapasite) { veri = (E[]) new Object[kapasite]; }
    public boolean isEmpty() { return top == -1; }
    public int size()        { return top + 1; }
    public E top()           { return isEmpty() ? null : veri[top]; }
    public void push(E x) {
        if (top == veri.length - 1) throw new IllegalStateException("Taşma");
        veri[++top] = x;
    }
    public E pop() {
        if (isEmpty()) throw new IllegalStateException("Alt taşma");
        E x = veri[top];
        veri[top--] = null;                  // referansı bırak
        return x;
    }
}

Jenerik (generic) dizi doğrudan oluşturulamadığı için Object[] dönüştürülür (derleyici “unchecked” uyarısı verir). Tüm işlemler O(1).

Gerçekleme · Kodu adım adım çalıştır

Kod İzleme: Dizi Tabanlı Yığın

veri (kapasite 4)[0]null[1]null[2]null[3]nulltop = −1 (boş)main() içindeki çağrılary.push(10)y.push(15)y.push(20)y.top()y.pop()y.push(25)y.push(30)y.push(40)

new DiziYigin<>(4): 4 hücrelik dizi, hepsi null; boş yığında top = −1.

veri (kapasite 4)[0]null[1]null[2]null[3]nulltop = −1 (boş)main() içindeki çağrılary.push(10)y.push(15)y.push(20)y.top()y.pop()y.push(25)y.push(30)y.push(40)

push(10): dolu mu? top = -1, veri.length − 1 = 3 → hayır, devam.

veri (kapasite 4)[0]10[1]null[2]null[3]null← top = 0main() içindeki çağrılary.push(10)✓y.push(15)y.push(20)y.top()y.pop()y.push(25)y.push(30)y.push(40)

veri[++top] = x: önce top -1 → 0, sonra veri[0] = 10. Tek atama → O(1).

veri (kapasite 4)[0]10[1]null[2]null[3]null← top = 0main() içindeki çağrılary.push(10)✓y.push(15)y.push(20)y.top()y.pop()y.push(25)y.push(30)y.push(40)

push(15): dolu mu? top = 0, veri.length − 1 = 3 → hayır, devam.

veri (kapasite 4)[0]10[1]15[2]null[3]null← top = 1main() içindeki çağrılary.push(10)✓y.push(15)✓y.push(20)y.top()y.pop()y.push(25)y.push(30)y.push(40)

veri[++top] = x: önce top 0 → 1, sonra veri[1] = 15. Tek atama → O(1).

veri (kapasite 4)[0]10[1]15[2]null[3]null← top = 1main() içindeki çağrılary.push(10)✓y.push(15)✓y.push(20)y.top()y.pop()y.push(25)y.push(30)y.push(40)

push(20): dolu mu? top = 1, veri.length − 1 = 3 → hayır, devam.

veri (kapasite 4)[0]10[1]15[2]20[3]null← top = 2main() içindeki çağrılary.push(10)✓y.push(15)✓y.push(20)✓y.top()y.pop()y.push(25)y.push(30)y.push(40)

veri[++top] = x: önce top 1 → 2, sonra veri[2] = 20. Tek atama → O(1).

veri (kapasite 4)[0]10[1]15[2]20[3]null← top = 2main() içindeki çağrılary.push(10)✓y.push(15)✓y.push(20)✓y.top()→ 20y.pop()y.push(25)y.push(30)y.push(40)

top(): yığın boş değil → veri[2] = 20 döner. Dizi ve top değişmez.

veri (kapasite 4)[0]10[1]15[2]20[3]null← top = 2main() içindeki çağrılary.push(10)✓y.push(15)✓y.push(20)✓y.top()→ 20y.pop()y.push(25)y.push(30)y.push(40)

pop(): isEmpty()? top = 2 ≠ −1 → boş değil, devam.

veri (kapasite 4)[0]10[1]15[2]20[3]null← top = 2main() içindeki çağrılary.push(10)✓y.push(15)✓y.push(20)✓y.top()→ 20y.pop()y.push(25)y.push(30)y.push(40)

E x = veri[top] → x = 20 (üstteki öğe saklanır).

veri (kapasite 4)[0]10[1]15[2]null[3]null← top = 1main() içindeki çağrılary.push(10)✓y.push(15)✓y.push(20)✓y.top()→ 20y.pop()y.push(25)y.push(30)y.push(40)

veri[top--] = null: hücre boşaltılır (nesne çöp toplayıcıya bırakılır), top → 1.

veri (kapasite 4)[0]10[1]15[2]null[3]null← top = 1main() içindeki çağrılary.push(10)✓y.push(15)✓y.push(20)✓y.top()→ 20y.pop()→ 20y.push(25)y.push(30)y.push(40)

return 20: son giren ilk çıktı (LIFO).

veri (kapasite 4)[0]10[1]15[2]null[3]null← top = 1main() içindeki çağrılary.push(10)✓y.push(15)✓y.push(20)✓y.top()→ 20y.pop()→ 20y.push(25)y.push(30)y.push(40)

push(25): dolu mu? top = 1, veri.length − 1 = 3 → hayır, devam.

veri (kapasite 4)[0]10[1]15[2]25[3]null← top = 2main() içindeki çağrılary.push(10)✓y.push(15)✓y.push(20)✓y.top()→ 20y.pop()→ 20y.push(25)✓y.push(30)y.push(40)

veri[++top] = x: önce top 1 → 2, sonra veri[2] = 25. Tek atama → O(1).

veri (kapasite 4)[0]10[1]15[2]25[3]null← top = 2main() içindeki çağrılary.push(10)✓y.push(15)✓y.push(20)✓y.top()→ 20y.pop()→ 20y.push(25)✓y.push(30)y.push(40)

push(30): dolu mu? top = 2, veri.length − 1 = 3 → hayır, devam.

veri (kapasite 4)[0]10[1]15[2]25[3]30← top = 3main() içindeki çağrılary.push(10)✓y.push(15)✓y.push(20)✓y.top()→ 20y.pop()→ 20y.push(25)✓y.push(30)✓y.push(40)

veri[++top] = x: önce top 2 → 3, sonra veri[3] = 30. Tek atama → O(1).

veri (kapasite 4)[0]10[1]15[2]25[3]30← top = 3main() içindeki çağrılary.push(10)✓y.push(15)✓y.push(20)✓y.top()→ 20y.pop()→ 20y.push(25)✓y.push(30)✓y.push(40)

push(40): dolu mu? top = 3, veri.length − 1 = 3 → evet, dolu!

veri (kapasite 4)[0]10[1]15[2]25[3]30← top = 3main() içindeki çağrılary.push(10)✓y.push(15)✓y.push(20)✓y.top()→ 20y.pop()→ 20y.push(25)✓y.push(30)✓y.push(40)✗ Taşma

Dizi dolu: IllegalStateException("Taşma") fırlatılır. Sabit kapasiteli yığında taşma budur; dinamik dizi burada diziyi büyütürdü.

public void push(E x) {
  if (top == veri.length - 1)
    throw new IllegalStateException("Taşma");
  veri[++top] = x;
}
public E pop() {
  if (isEmpty())
    throw new IllegalStateException("Alt taşma");
  E x = veri[top];
  veri[top--] = null;          // referansı bırak
  return x;
}
public E top() { return isEmpty() ? null : veri[top]; }

Gerçekleme

Dinamik Dizi: Dolunca İki Katına Büyütme

public void push(E x) {
  if (top == veri.length - 1)
    buyut(2 * veri.length);
  veri[++top] = x;
}
private void buyut(int yeniKap) {
  E[] yeni = (E[]) new Object[yeniKap];
  for (int i = 0; i <= top; i++)
    yeni[i] = veri[i];
  veri = yeni;
}
  • Dizi dolunca iki katı kapasiteli yeni dizi ayrılır ve öğeler kopyalanır: büyütme yapan push O(n) sürer.
  • Büyütmeler seyrektir: n push'ta kopyalar 2 + 4 + 8 + … < 2n, toplam maliyet < 3n → push amortize O(1).
  • Her dolumda sabit sayıda (+c) hücre eklenirse toplam O(n²), push başına O(n) olur; kapasite oranla büyütülmelidir.

Amortize maliyet tek bir işlemin değil, uzun bir işlem dizisinin ortalamasıdır.

Gerçekleme

Bağlı Liste Tabanlı Yığın

  • Öğeler birbirine bağlı düğümlerden oluşan bir zincirde tutulur → esnek boyut.
  • Boyut, çalışma zamanında ihtiyaca göre değişir; taşma olmaz (bellek bitene kadar).
  • Her düğümde bir referans (sonraki) tutulduğu için ek bellek kullanır.
  • Rastgele erişim yoktur, ama üstteki öğeye (listenin başı) erişim hızlı ve basittir.
  • Ekleme ve çıkarma listenin başında yapıldığı için O(1)'dir.
302010nulltoppush / pop yalnızcabaştaki (top) düğümde
class Dugum {
    int veri;
    Dugum sonraki;
    Dugum(int veri) { this.veri = veri; }   // sonraki = null
}

Neden başa ekliyoruz? Tek yönlü listede sondan silmek için sondan bir önceki düğümü bulmak gerekir → O(n).

Gerçekleme · Kodu adım adım çalıştır

Kod İzleme: Bağlı Liste ile push

top = nulldüğüm = [veri | sonraki]

Başlangıç: top = null, uzunluk = 0 → boş yığın.

10nulltop = nullgecicidüğüm = [veri | sonraki]

push(10): new Dugum(10) oluşturulur; gecici onu gösterir, sonraki alanı varsayılan olarak null.

10nulltop = nullgecicidüğüm = [veri | sonraki]

gecici.sonraki = top: top null olduğu için sonraki yine null (yeni düğüm tek başına).

10nulltopgecicidüğüm = [veri | sonraki]

top = gecici: yığının üstü artık yeni düğüm.

10nulltopgecicidüğüm = [veri | sonraki]

uzunluk++ → 1. Metot biter; yerel gecici kaybolur, düğüm top üzerinden erişilir.

15null10nulltopgecicidüğüm = [veri | sonraki]

push(15): yeni düğüm 15 oluşturulur. Henüz listeye bağlı değil; top hâlâ 10'u gösterir.

1510nulltopgecicidüğüm = [veri | sonraki]

gecici.sonraki = top: 15, eski üst olan 10'a bağlanır. Önce bağlarız ki eski liste kaybolmasın.

1510nulltopgecicidüğüm = [veri | sonraki]

top = gecici: yeni üst 15. Listenin başına ekleme, kaydırma yok → O(1).

1510nulltopgecicidüğüm = [veri | sonraki]

uzunluk++ → 2.

1510nulltopdüğüm = [veri | sonraki]

push biter. Yığın (üstten alta): 15 → 10. Son eklenen en üstte.

Dugum top;         // yığının üstü
int uzunluk;

public void push(int veri) {
  Dugum gecici = new Dugum(veri);
  gecici.sonraki = top;
  top = gecici;
  uzunluk++;
}

Gerçekleme · Kodu adım adım çalıştır

Kod İzleme: Bağlı Liste ile pop

1510nulltopdüğüm = [veri | sonraki]

Yığında 15 ve 10 var. Döngü pop()'u 3 kez çağıracak.

1510nulltopdüğüm = [veri | sonraki]

pop(): önce boşluk kontrolü; top null değil → devam.

1510nulltopdüğüm = [veri | sonraki]

sonuc = top.veri → 15 saklanır (düğüm kopmadan önce).

1510nulltopdüğüm = [veri | sonraki]

top = top.sonraki: top bir aşağı iner → düğüm(10). Eski üst düğüme artık referans yok.

1510nulltopdüğüm = [veri | sonraki]

uzunluk-- → 1.

1510nulltopdüğüm = [veri | sonraki]

return 15. Erişilemeyen düğümü çöp toplayıcı temizler.

10nulltopdüğüm = [veri | sonraki]

main, dönen 15 değerini yazdırır.

10nulltopdüğüm = [veri | sonraki]

pop(): önce boşluk kontrolü; top null değil → devam.

10nulltopdüğüm = [veri | sonraki]

sonuc = top.veri → 10 saklanır (düğüm kopmadan önce).

10nulltop = nulldüğüm = [veri | sonraki]

top = top.sonraki: top bir aşağı iner → null. Eski üst düğüme artık referans yok.

10nulltop = nulldüğüm = [veri | sonraki]

uzunluk-- → 0.

10nulltop = nulldüğüm = [veri | sonraki]

return 10. Erişilemeyen düğümü çöp toplayıcı temizler.

top = nulldüğüm = [veri | sonraki]

main, dönen 10 değerini yazdırır.

top = nulldüğüm = [veri | sonraki]

pop(): top == null → yığın boş!

top = nulldüğüm = [veri | sonraki]

Alt taşma istisnası; program durur. Kontrol olmasaydı top.veri NullPointerException verirdi.

public int pop() {
  if (top == null)
    throw new RuntimeException("boş");
  int sonuc = top.veri;
  top = top.sonraki;
  uzunluk--;
  return sonuc;
}
// main içinde (yığın: 15 → 10):
for (int k = 0; k < 3; k++)
  System.out.println(pop());

Gerçekleme

Dizi mi, Bağlı Liste mi?

ÖlçütDizi tabanlıBağlı liste tabanlı
Boyutsabit kapasite (dinamik dizide büyütülür)esnek, çalışma zamanında değişir
push / popO(1) (büyütmede amortize O(1))O(1)
Taşmataşma mümkünyalnızca bellek biterse
Öğe başına bellekyalnızca veriveri + sonraki referansı
Boş yerkullanılmayan kapasiteyok
JavaArrayDeque, Stack (Vector tabanlı)LinkedList (Deque olarak)
Java'da yeni kodda Deque<Integer> y = new ArrayDeque<>(); ile push/pop/peek önerilir; eski Stack sınıfı senkronize olduğu için daha yavaştır.

Yığın Uygulamaları

Yığınların Kullanıldığı Yerler

İfadelerInfix, prefix, postfix dönüşümü ve değerlendirmesi; parantez kontrolü.
Geri al / yineleMetin düzenleyicilerde undo ve redo işlemleri.
Tarayıcı geçmişi“Geri” ve “ileri” düğmeleri iki yığınla yönetilir.
Çağrı yığınıFonksiyon çağrıları, yerel değişkenler ve dönüş adresleri (call stack).
Geri izlemeLabirentte yol bulma, N-Vezir, Şövalye Turu (backtracking).
AlgoritmalarHanoi kuleleri, ağaç gezinme, stock span, histogram problemleri.
Çizge algoritmalarıDerinlik öncelikli arama, topolojik sıralama, güçlü bağlantılı bileşenler.
Dize ters çevirmeKarakterler sırayla eklenir, ters sırayla çıkar.
Sonraki büyük öğeHer öğe için sağındaki ilk büyük değer.

Yığın Uygulamaları

Tarayıcıda Geri ve İleri: İki Yığın

başlangıçboşgeri yığınıboşileri yığınışu anki sayfaAna sayfa

Tarayıcı açıldı: şu anki sayfa Ana sayfa; iki yığın da boş.

ziyaret("Haberler")Ana sayfageri yığınıboşileri yığınışu anki sayfaHaberler

ziyaret(Haberler): eski sayfa geri yığınına push, yeni sayfa açılır.

ziyaret("Spor")Ana sayfaHaberlergeri yığınıboşileri yığınışu anki sayfaSpor

ziyaret(Spor): eski sayfa geri yığınına push, yeni sayfa açılır.

geri()Ana sayfageri yığınıSporileri yığınışu anki sayfaHaberler

geri(): şu anki sayfa ileri yığınına push; geri yığınından pop → Haberler.

geri()boşgeri yığınıSporHaberlerileri yığınışu anki sayfaAna sayfa

geri(): şu anki sayfa ileri yığınına push; geri yığınından pop → Ana sayfa.

ileri()Ana sayfageri yığınıSporileri yığınışu anki sayfaHaberler

ileri(): şu anki sayfa geri yığınına push; ileri yığınından pop → Haberler.

ziyaret("Hava")Ana sayfaHaberlergeri yığınıboşileri yığınışu anki sayfaHavatemizlendi: Spor

ziyaret(Hava): eski sayfa geri yığınına push, yeni sayfa açılır. Yeni bir yola girildiği için ileri yığını boşaltılır.

geri()Ana sayfageri yığınıHavaileri yığınışu anki sayfaHaberler

geri(): şu anki sayfa ileri yığınına push; geri yığınından pop → Haberler.

Kural: ziyaret → geri'ye push, ileri'yi temizle · geri → ileri'ye push, geri'den pop · ileri → geri'ye push, ileri'den pop. Her işlem O(1). Metin düzenleyicideki geri al / yinele (undo/redo) aynı düzeni kullanır.

Yığın Uygulamaları · Kodu adım adım çalıştır

Çağrı Yığını: fakt(3) Adım Adım

main()çağrı yığını (call stack)main() çalışıyorçerçeve = parametreler+ yerel değişkenler+ dönüş adresi

main çalışıyor; fakt(3) çağrılacak. Her çağrı yığına yeni bir çerçeve (frame) iter.

main()fakt(n=3)çağrı yığını (call stack)push: fakt(3)yığın derinliği = 1

fakt(3) çerçevesi yığına push edildi. n <= 1? 3 <= 1 → false.

main()fakt(n=3)3 * fakt(2) = ?çağrı yığını (call stack)fakt(3) askıdaönce fakt(2) bitmeli

3 * fakt(2) hesaplanamaz: önce fakt(2) bitmeli. Bu çerçeve askıda bekler.

main()fakt(n=3)3 * fakt(2) = ?fakt(n=2)çağrı yığını (call stack)push: fakt(2)yığın derinliği = 2

fakt(2) çerçevesi yığına push edildi. n <= 1? 2 <= 1 → false.

main()fakt(n=3)3 * fakt(2) = ?fakt(n=2)2 * fakt(1) = ?çağrı yığını (call stack)fakt(2) askıdaönce fakt(1) bitmeli

2 * fakt(1) hesaplanamaz: önce fakt(1) bitmeli. Bu çerçeve askıda bekler.

main()fakt(n=3)3 * fakt(2) = ?fakt(n=2)2 * fakt(1) = ?fakt(n=1)çağrı yığını (call stack)push: fakt(1)yığın derinliği = 3

fakt(1) çerçevesi yığına push edildi. n <= 1? 1 <= 1 → true.

main()fakt(n=3)3 * fakt(2) = ?fakt(n=2)2 * fakt(1) = ?fakt(n=1)çağrı yığını (call stack)taban durumreturn 1

Taban durum: return 1. Artık yığın çözülmeye (unwinding) başlar.

main()fakt(n=3)3 * fakt(2) = ?fakt(n=2)2 * 1 = 2çağrı yığını (call stack)fakt(n=1) → 1pop: fakt(1) → 12 * 1 = 2

fakt(1) pop edildi, 1 döndü. fakt(2) devam eder: 2 * 1 = 2.

main()fakt(n=3)3 * 2 = 6çağrı yığını (call stack)fakt(n=2) → 2pop: fakt(2) → 23 * 2 = 6

fakt(2) pop edildi, 2 döndü. fakt(3) devam eder: 3 * 2 = 6.

main()çağrı yığını (call stack)fakt(n=3) → 6pop: fakt(3) → 6println(6)

fakt(3) = 6 yazdırılır. Derinlik n → O(n) bellek; çok derin özyineleme StackOverflowError verir.

int fakt(int n) {
  if (n <= 1)
    return 1;
  return n * fakt(n - 1);
}
// main içinde:
System.out.println(fakt(3));

Yığın Uygulamaları · Kodu adım adım çalıştır

Dize Tersine Çevirme: Adım Adım

str = "ABCD"

main, tersCevir("ABCD") çağırır.

str = "ABCD"boşyigin

Boş bir karakter yığını oluşturulur.

karakterler0A1B2C3Dboşyigin

Dize karakter dizisine kopyalanır (üzerine yazacağız).

karakterler0A1B2C3D▲ cAyigin

1. döngü: c = 'A' → push('A').

karakterler0A1B2C3D▲ cAByigin

1. döngü: c = 'B' → push('B').

karakterler0A1B2C3D▲ cABCyigin

1. döngü: c = 'C' → push('C').

karakterler0A1B2C3D▲ cABCDyigin

1. döngü: c = 'D' → push('D'). Tüm karakterler yığında; en üstte son karakter.

karakterler0D1B2C3D▲ iABCDyigin

2. döngü, i = 0: pop() → 'D', karakterler[0] = 'D'. Çıkış sırası girişin tersi.

karakterler0D1C2C3D▲ iABCyigin

2. döngü, i = 1: pop() → 'C', karakterler[1] = 'C'. Çıkış sırası girişin tersi.

karakterler0D1C2B3D▲ iAByigin

2. döngü, i = 2: pop() → 'B', karakterler[2] = 'B'. Çıkış sırası girişin tersi.

karakterler0D1C2B3A▲ iAyigin

2. döngü, i = 3: pop() → 'A', karakterler[3] = 'A'. Çıkış sırası girişin tersi.

karakterler0D1C2B3Aboşyigin

return "DCBA". Zaman ve ek bellek O(n).

karakterler0D1C2B3Aboşyigin

main sonucu yazdırır: DCBA.

String tersCevir(String str) {
    Deque<Character> yigin = new ArrayDeque<>();
    char[] karakterler = str.toCharArray();
    for (char c : karakterler)
        yigin.push(c);
    for (int i = 0; i < str.length(); i++)
        karakterler[i] = yigin.pop();
    return new String(karakterler);
}
// main içinde:
System.out.println(tersCevir("ABCD"));

Yığın Uygulamaları

Parantez Kontrolü

S dizisi yalnızca ( ) { } [ ] karakterlerinden oluşsun. S geçerlidir ancak ve ancak:

  1. her açık parantez aynı tip bir parantezle kapatılmışsa ve
  2. parantezler doğru sırada kapatılmışsa (en son açılan ilk kapanır → LIFO!).
Algoritma: eşleşmemiş açık parantezler yığında tutulur. S baştan sona gezilir:
  • Açık parantez → yığına push.
  • Kapalı parantez → yığın boşsa geçersiz; değilse pop, çıkan aynı tipte değilse geçersiz.
  • S bitince yığın boşsa geçerli, değilse geçersiz (kapanmamış parantez var).
{[()]}geçerli
([)]sıra yanlış
(()kapanmamış
())fazla kapanış

Yığın Uygulamaları

Parantez Kontrolü: Java

boolean gecerliMi(String s) {
  Deque<Character> yigin = new ArrayDeque<>();
  for (char c : s.toCharArray()) {
    if (c == '(' || c == '[' || c == '{') {
      yigin.push(c);
    } else {
      if (yigin.isEmpty()) return false;
      char acik = yigin.pop();
      if (!eslesir(acik, c)) return false;
    }
  }
  return yigin.isEmpty();
}
Yardımcı metot
boolean eslesir(char a, char k) {
  return (a == '(' && k == ')')
      || (a == '[' && k == ']')
      || (a == '{' && k == '}');
}
Boş yığın kontrolüOlmasaydı "())" ya da "]" girdisinde boş yığından pop → istisna.
KarmaşıklıkZaman O(n), en kötü durumda yığın O(n) bellek.

Yığın Uygulamaları · Kodu adım adım çalıştır

Kod İzleme: gecerliMi("{{[]}()}[]")

s0{1{2[3]4}5(6)7}8[9]boşyigin

Eşleşmemiş açık parantezleri tutacak boş yığın oluşturulur.

s0{1{2[3]4}5(6)7}8[9]▲ c{yigin

'{' açık parantez → push. Kapanışını bekleyecek.

s0{1{2[3]4}5(6)7}8[9]▲ c{{yigin

'{' açık parantez → push. Kapanışını bekleyecek.

s0{1{2[3]4}5(6)7}8[9]▲ c{{[yigin

'[' açık parantez → push. Kapanışını bekleyecek.

s0{1{2[3]4}5(6)7}8[9]▲ c{{[yigin

']' kapalı parantez: yığın boş değil → pop, en son açılan '[' çıkar.

s0{1{2[3]4}5(6)7}8[9]▲ c{{yigin

eslesir('[', ']') → true: aynı tip, doğru sıra. Devam.

s0{1{2[3]4}5(6)7}8[9]▲ c{{yigin

'}' kapalı parantez: yığın boş değil → pop, en son açılan '{' çıkar.

s0{1{2[3]4}5(6)7}8[9]▲ c{yigin

eslesir('{', '}') → true: aynı tip, doğru sıra. Devam.

s0{1{2[3]4}5(6)7}8[9]▲ c{(yigin

'(' açık parantez → push. Kapanışını bekleyecek.

s0{1{2[3]4}5(6)7}8[9]▲ c{(yigin

')' kapalı parantez: yığın boş değil → pop, en son açılan '(' çıkar.

s0{1{2[3]4}5(6)7}8[9]▲ c{yigin

eslesir('(', ')') → true: aynı tip, doğru sıra. Devam.

s0{1{2[3]4}5(6)7}8[9]▲ c{yigin

'}' kapalı parantez: yığın boş değil → pop, en son açılan '{' çıkar.

s0{1{2[3]4}5(6)7}8[9]▲ cboşyigin

eslesir('{', '}') → true: aynı tip, doğru sıra. Devam.

s0{1{2[3]4}5(6)7}8[9]▲ c[yigin

'[' açık parantez → push. Kapanışını bekleyecek.

s0{1{2[3]4}5(6)7}8[9]▲ c[yigin

']' kapalı parantez: yığın boş değil → pop, en son açılan '[' çıkar.

s0{1{2[3]4}5(6)7}8[9]▲ cboşyigin

eslesir('[', ']') → true: aynı tip, doğru sıra. Devam.

s0{1{2[3]4}5(6)7}8[9]boşyigin

Dize bitti, yığın boş → return true: dize geçerli. Her karakter bir kez işlendi → O(n).

boolean gecerliMi(String s) {
  Deque<Character> yigin = new ArrayDeque<>();
  for (char c : s.toCharArray()) {
    if (c == '(' || c == '[' || c == '{') {
      yigin.push(c);
    } else {
      if (yigin.isEmpty()) return false;
      char acik = yigin.pop();
      if (!eslesir(acik, c)) return false;
    }
  }
  return yigin.isEmpty();
}

Yığın Uygulamaları · Kodu adım adım çalıştır

Kod İzleme: gecerliMi("([)]")

s0(1[2)3]boşyigin

Eşleşmemiş açık parantezleri tutacak boş yığın oluşturulur.

s0(1[2)3]▲ c(yigin

'(' açık parantez → push. Kapanışını bekleyecek.

s0(1[2)3]▲ c([yigin

'[' açık parantez → push. Kapanışını bekleyecek.

s0(1[2)3]▲ c([yigin

')' kapalı parantez: yığın boş değil → pop, en son açılan '[' çıkar.

s0(1[2)3]▲ c([yigineşleşmedi!

eslesir('[', ')') → false: ')' kapanıyor ama en son açılan '['. return false. Benzer biçimde (() sonda yığın dolu kaldığı, ()) ise boş yığında kapanış geldiği için geçersizdir.

boolean gecerliMi(String s) {
  Deque<Character> yigin = new ArrayDeque<>();
  for (char c : s.toCharArray()) {
    if (c == '(' || c == '[' || c == '{') {
      yigin.push(c);
    } else {
      if (yigin.isEmpty()) return false;
      char acik = yigin.pop();
      if (!eslesir(acik, c)) return false;
    }
  }
  return yigin.isEmpty();
}

Yığın Uygulamaları

Sonraki Büyük Öğe (Next Greater Element)

Dizideki her öğe için sağında kalan, kendisinden büyük ilk öğeyi bul; yoksa −1.

dizi473481
sonuc7848−1−1
  • Kaba kuvvet: her öğe için sağını tara → O(n²).
  • Yığınla: dizi sağdan sola gezilir; yığında yalnızca “aday” büyük değerler kalır → O(n).
  • Yığın, alttan üste doğru azalan sırada kalır (monoton yığın).
int[] sonrakiBuyuk(int[] dizi) {
    int[] sonuc = new int[dizi.length];
    Deque<Integer> yigin = new ArrayDeque<>();
    for (int i = dizi.length-1; i >= 0; i--) {
        while (!yigin.isEmpty() &&
               yigin.peek() <= dizi[i])
            yigin.pop();
        sonuc[i] = yigin.isEmpty()
                 ? -1 : yigin.peek();
        yigin.push(dizi[i]);
    }
    return sonuc;
}

Yığın Uygulamaları · Kodu adım adım çalıştır

Kod İzleme: sonrakiBuyuk({4, 7, 3, 4, 8, 1})

dizisonuc041723344851000000boşyigin

sonuc (hepsi 0) ve boş yığın oluşturulur. Dizi sağdan sola gezilecek.

dizisonuc041723344851▲ i00000-1boşyigin

i = 5 (dizi[i] = 1): yığın boş → −1.

dizisonuc041723344851▲ i00000-11yigin

push(1): soldaki öğeler için yeni aday. Yığın alttan üste azalan kalır.

dizisonuc041723344851▲ i00000-11yigin

i = 4: üstteki 1 ≤ 8 → pop. 1, 8'in solundakiler için asla cevap olamaz (arada daha büyük 8 var).

dizisonuc041723344851▲ i0000-1-1boşyigin

i = 4 (dizi[i] = 8): yığın boş → −1.

dizisonuc041723344851▲ i0000-1-18yigin

push(8): soldaki öğeler için yeni aday. Yığın alttan üste azalan kalır.

dizisonuc041723344851▲ i0008-1-18yigin

i = 3 (dizi[i] = 4): üstteki 8 > 4 → sonuc[3] = 8.

dizisonuc041723344851▲ i0008-1-184yigin

push(4): soldaki öğeler için yeni aday. Yığın alttan üste azalan kalır.

dizisonuc041723344851▲ i0048-1-184yigin

i = 2 (dizi[i] = 3): üstteki 4 > 3 → sonuc[2] = 4.

dizisonuc041723344851▲ i0048-1-1843yigin

push(3): soldaki öğeler için yeni aday. Yığın alttan üste azalan kalır.

dizisonuc041723344851▲ i0048-1-1843yigin

i = 1: üstteki 3 ≤ 7 → pop. 3, 7'nin solundakiler için asla cevap olamaz (arada daha büyük 7 var).

dizisonuc041723344851▲ i0048-1-184yigin

i = 1: üstteki 4 ≤ 7 → pop. 4, 7'nin solundakiler için asla cevap olamaz (arada daha büyük 7 var).

dizisonuc041723344851▲ i0848-1-18yigin

i = 1 (dizi[i] = 7): üstteki 8 > 7 → sonuc[1] = 8.

dizisonuc041723344851▲ i0848-1-187yigin

push(7): soldaki öğeler için yeni aday. Yığın alttan üste azalan kalır.

dizisonuc041723344851▲ i7848-1-187yigin

i = 0 (dizi[i] = 4): üstteki 7 > 4 → sonuc[0] = 7.

dizisonuc041723344851▲ i7848-1-1874yigin

push(4): soldaki öğeler için yeni aday. Yığın alttan üste azalan kalır.

dizisonuc0417233448517848-1-1874yigin

return [7, 8, 4, 8, -1, -1]. Her öğe en fazla bir kez push ve bir kez pop edilir → O(n).

int[] sonrakiBuyuk(int[] dizi) {
    int[] sonuc = new int[dizi.length];
    Deque<Integer> yigin = new ArrayDeque<>();
    for (int i = dizi.length-1; i >= 0; i--) {
        while (!yigin.isEmpty() &&
               yigin.peek() <= dizi[i])
            yigin.pop();
        sonuc[i] = yigin.isEmpty()
                 ? -1 : yigin.peek();
        yigin.push(dizi[i]);
    }
    return sonuc;
}

Yığın Uygulamaları · Kodu adım adım çalıştır

Kod İzleme: getMin() O(1) Olan Yığın

boşyboşminpush(5)push(3)push(7)push(3)getMin()pop()getMin()pop()pop()getMin()min'in üstü = o anki en küçük

İki yığın: y tüm öğeleri, min ise her an en küçüğü üstte tutar. Amaç: getMin() O(1).

5yboşminpush(5)push(3)push(7)push(3)getMin()pop()getMin()pop()pop()getMin()min'in üstü = o anki en küçük

push(5): y.push(5).

5y5minpush(5)push(3)push(7)push(3)getMin()pop()getMin()pop()pop()getMin()min'in üstü = o anki en küçük

min boş → min.push(5): yeni (ya da eşit) en küçük.

53y5minpush(5)push(3)push(7)push(3)getMin()pop()getMin()pop()pop()getMin()min'in üstü = o anki en küçük

push(3): y.push(3).

53y53minpush(5)push(3)push(7)push(3)getMin()pop()getMin()pop()pop()getMin()min'in üstü = o anki en küçük

3 <= 5 → true → min.push(3): yeni (ya da eşit) en küçük.

537y53minpush(5)push(3)push(7)push(3)getMin()pop()getMin()pop()pop()getMin()min'in üstü = o anki en küçük

push(7): y.push(7).

537y53minpush(5)push(3)push(7)push(3)getMin()pop()getMin()pop()pop()getMin()min'in üstü = o anki en küçük

7 <= 3 → false: 7 en küçük değil, min yığını değişmez.

5373y53minpush(5)push(3)push(7)push(3)getMin()pop()getMin()pop()pop()getMin()min'in üstü = o anki en küçük

push(3): y.push(3).

5373y533minpush(5)push(3)push(7)push(3)getMin()pop()getMin()pop()pop()getMin()min'in üstü = o anki en küçük

3 <= 3 → true → min.push(3): yeni (ya da eşit) en küçük.

5373y533minpush(5)push(3)push(7)push(3)getMin()→ 3pop()getMin()pop()pop()getMin()min'in üstü = o anki en küçük

getMin(): min.peek() = 3. Arama yok → O(1).

5373y533minpush(5)push(3)push(7)push(3)getMin()→ 3pop()→ 3getMin()pop()pop()getMin()min'in üstü = o anki en küçük

pop(): y'den 3 çıktı. 3 == min.peek()? evet → min'den de pop; yeni en küçük 3.

537y53minpush(5)push(3)push(7)push(3)getMin()→ 3pop()→ 3getMin()→ 3pop()pop()getMin()min'in üstü = o anki en küçük

getMin(): min.peek() = 3. Arama yok → O(1).

537y53minpush(5)push(3)push(7)push(3)getMin()→ 3pop()→ 3getMin()→ 3pop()→ 7pop()getMin()min'in üstü = o anki en küçük

pop(): y'den 7 çıktı. 7 == min.peek()? hayır → min değişmez.

53y53minpush(5)push(3)push(7)push(3)getMin()→ 3pop()→ 3getMin()→ 3pop()→ 7pop()→ 3getMin()min'in üstü = o anki en küçük

pop(): y'den 3 çıktı. 3 == min.peek()? evet → min'den de pop; yeni en küçük 5.

5y5minpush(5)push(3)push(7)push(3)getMin()→ 3pop()→ 3getMin()→ 3pop()→ 7pop()→ 3getMin()→ 5min'in üstü = o anki en küçük

getMin(): min.peek() = 5. Arama yok → O(1). <= sayesinde ikinci 3 çıkınca en küçük hâlâ 3 kalmıştı.

Deque<Integer> y = new ArrayDeque<>();
Deque<Integer> min = new ArrayDeque<>();
void push(int x) {
  y.push(x);
  if (min.isEmpty() || x <= min.peek())
    min.push(x);
}
int pop() {
  int x = y.pop();
  if (x == min.peek()) min.pop();
  return x;
}
int getMin() { return min.peek(); }

Hanoi Kuleleri

Hanoi Kuleleri: Yığın ile Özyinelemesiz Çözüm

Kural: her hamlede en üstteki tek disk taşınır; büyük disk küçüğün üstüne konamaz.

// Gorev: n diski k(aynak)'tan h(edef)'e, y(ardımcı) ile taşı
record Gorev(int n, char k, char y, char h) {}

void hanoi(int n) {
  Deque<Gorev> yigin = new ArrayDeque<>();
  yigin.push(new Gorev(n, 'A', 'B', 'C'));
  while (!yigin.isEmpty()) {
    Gorev g = yigin.pop();
    if (g.n() == 1)
      System.out.println(g.k() + " → " + g.h());
    else {                         // ters sırayla
      yigin.push(new Gorev(g.n()-1, g.y(), g.k(), g.h()));
      yigin.push(new Gorev(1, g.k(), g.y(), g.h()));
      yigin.push(new Gorev(g.n()-1, g.k(), g.h(), g.y()));
    }
  }
}
Özyinelemeli çözümdeki çağrı yığınının yerini açık bir görev yığını alır. n > 1 ise üç alt görev ters sırayla eklenir: 1. n−1 diski kaynaktan yardımcıya, 2. 1 diski kaynaktan hedefe, 3. n−1 diski yardımcıdan hedefe; böylece 1. görev en üstte olur.

Hanoi Kuleleri · Kodu adım adım çalıştır

Kod İzleme: hanoi(3) (yığınla)

(3,A,B,C)yigin (n,k,y,h)A321BC

Başlangıç görevi (3, A, B, C): “3 diski A'dan C'ye, B yardımcı ile taşı”.

(3,A,B,C)yigin (n,k,y,h)A321BC

pop → g = (3,A,B,C): şimdi bu görev yapılacak.

(2,B,A,C)(1,A,B,C)(2,A,C,B)yigin (n,k,y,h)A321BC

n = 3 > 1 → üç alt görev ters sırayla push edilir; ilk yapılacak (2, A, C, B) en üstte kalır (LIFO).

(2,B,A,C)(1,A,B,C)(2,A,C,B)yigin (n,k,y,h)A321BC

pop → g = (2,A,C,B): şimdi bu görev yapılacak.

(2,B,A,C)(1,A,B,C)(1,C,A,B)(1,A,C,B)(1,A,B,C)yigin (n,k,y,h)A321BC

n = 2 > 1 → üç alt görev ters sırayla push edilir; ilk yapılacak (1, A, B, C) en üstte kalır (LIFO).

(2,B,A,C)(1,A,B,C)(1,C,A,B)(1,A,C,B)(1,A,B,C)yigin (n,k,y,h)A321BC

pop → g = (1,A,B,C): şimdi bu görev yapılacak.

(2,B,A,C)(1,A,B,C)(1,C,A,B)(1,A,C,B)yigin (n,k,y,h)A32BC1

n = 1 → doğrudan taşı: disk 1, A → C (hamle 1).

(2,B,A,C)(1,A,B,C)(1,C,A,B)(1,A,C,B)yigin (n,k,y,h)A32BC1

pop → g = (1,A,C,B): şimdi bu görev yapılacak.

(2,B,A,C)(1,A,B,C)(1,C,A,B)yigin (n,k,y,h)A3B2C1

n = 1 → doğrudan taşı: disk 2, A → B (hamle 2).

(2,B,A,C)(1,A,B,C)(1,C,A,B)yigin (n,k,y,h)A3B2C1

pop → g = (1,C,A,B): şimdi bu görev yapılacak.

(2,B,A,C)(1,A,B,C)yigin (n,k,y,h)A3B21C

n = 1 → doğrudan taşı: disk 1, C → B (hamle 3).

void hanoi(int n) {
  Deque<Gorev> yigin = new ArrayDeque<>();
  yigin.push(new Gorev(n, 'A', 'B', 'C'));
  while (!yigin.isEmpty()) {
    Gorev g = yigin.pop();
    if (g.n() == 1)
      System.out.println(g.k() + " → " + g.h());
    else {                         // ters sırayla
      yigin.push(new Gorev(g.n()-1, g.y(), g.k(), g.h()));
      yigin.push(new Gorev(1, g.k(), g.y(), g.h()));
      yigin.push(new Gorev(g.n()-1, g.k(), g.h(), g.y()));
    }
  }
}

Hanoi Kuleleri · Kodu adım adım çalıştır

Kod İzleme: hanoi(3) (yığınla, devam)

(2,B,A,C)(1,A,B,C)yigin (n,k,y,h)A3B21C

pop → g = (1,A,B,C): şimdi bu görev yapılacak.

(2,B,A,C)yigin (n,k,y,h)AB21C3

n = 1 → doğrudan taşı: disk 3, A → C (hamle 4).

(2,B,A,C)yigin (n,k,y,h)AB21C3

pop → g = (2,B,A,C): şimdi bu görev yapılacak.

(1,A,B,C)(1,B,A,C)(1,B,C,A)yigin (n,k,y,h)AB21C3

n = 2 > 1 → üç alt görev ters sırayla push edilir; ilk yapılacak (1, B, C, A) en üstte kalır (LIFO).

(1,A,B,C)(1,B,A,C)(1,B,C,A)yigin (n,k,y,h)AB21C3

pop → g = (1,B,C,A): şimdi bu görev yapılacak.

(1,A,B,C)(1,B,A,C)yigin (n,k,y,h)A1B2C3

n = 1 → doğrudan taşı: disk 1, B → A (hamle 5).

(1,A,B,C)(1,B,A,C)yigin (n,k,y,h)A1B2C3

pop → g = (1,B,A,C): şimdi bu görev yapılacak.

(1,A,B,C)yigin (n,k,y,h)A1BC32

n = 1 → doğrudan taşı: disk 2, B → C (hamle 6).

(1,A,B,C)yigin (n,k,y,h)A1BC32

pop → g = (1,A,B,C): şimdi bu görev yapılacak.

boşyigin (n,k,y,h)ABC321

n = 1 → doğrudan taşı: disk 1, A → C (hamle 7).

boşyigin (n,k,y,h)ABC321

Yığın boş → döngü biter. 7 = 2³ − 1 hamle; tüm diskler C'de.

void hanoi(int n) {
  Deque<Gorev> yigin = new ArrayDeque<>();
  yigin.push(new Gorev(n, 'A', 'B', 'C'));
  while (!yigin.isEmpty()) {
    Gorev g = yigin.pop();
    if (g.n() == 1)
      System.out.println(g.k() + " → " + g.h());
    else {                         // ters sırayla
      yigin.push(new Gorev(g.n()-1, g.y(), g.k(), g.h()));
      yigin.push(new Gorev(1, g.k(), g.y(), g.h()));
      yigin.push(new Gorev(g.n()-1, g.k(), g.h(), g.y()));
    }
  }
}

Stock Span

Stock Span (Hisse Senedi Sıçrama) Problemi

  • Günlük kapanış fiyatları bir dizide verilir.
  • Bir günün span değeri: o gün dahil, geriye doğru fiyatı bugünkü fiyattan küçük ya da eşit olan ardışık günlerin sayısı.
  • Finansal veri analizinde bir fiyatın “kaç gündür aşılmadığını” gösterir.
GünFiyatSpanNeden?
11001ilk gün
470260 ≤ 70, 80 > 70
675460, 70, 60 ≤ 75; 80 > 75
785675, 60, 70, 60, 80 ≤ 85
100g1180g2160g3170g4260g5175g6485g76

Fiyatlar [100, 80, 60, 70, 60, 75, 85] → span [1, 1, 1, 2, 1, 4, 6]. Çubukların altındaki kalın sayılar span değerleridir.

Stock Span

Stock Span: Yığın ile O(n) Çözüm

int[] span(int[] fiyatlar) {
    int[] sicrama = new int[fiyatlar.length];
    Deque<Integer> yigin = new ArrayDeque<>();
    yigin.push(0); sicrama[0] = 1;
    for (int i = 1; i < fiyatlar.length; i++) {
        while (!yigin.isEmpty() &&
            fiyatlar[i] >= fiyatlar[yigin.peek()])
            yigin.pop();
        sicrama[i] = yigin.isEmpty() ? i + 1
                              : i - yigin.peek();
        yigin.push(i);
    }
    return sicrama;
}
Yığında ne var?Henüz aşılmamış günlerin indisleri (fiyatlar alttan üste azalır).
SpanÜst = önceki ilk daha yüksek gün: span = i − üst; yığın boşsa i + 1.
KarmaşıklıkHer indis bir kez eklenir, en çok bir kez çıkar → O(n).

Stock Span · Kodu adım adım çalıştır

Kod İzleme: span({100, 80, 60, 70, 60, 75, 85})

100018016027036047558560 (100)yigin

İlk gün: indis 0 yığına, sicrama[0] = 1 (öncesinde gün yok).

1000180116027036047558560 (100)yigin

i = 1 (fiyat 80): üstteki 0. gün (100) daha yüksek → span = 1 − 0 = 1.

1000180116027036047558560 (100)1 (80)yigin

push(1): sonraki günler için olası sınır.

10001801160217036047558560 (100)1 (80)yigin

i = 2 (fiyat 60): üstteki 1. gün (80) daha yüksek → span = 2 − 1 = 1.

10001801160217036047558560 (100)1 (80)2 (60)yigin

push(2): sonraki günler için olası sınır.

10001801160217036047558560 (100)1 (80)2 (60)yigin

i = 3: 70 ≥ fiyatlar[2] = 60 → pop. 2. gün artık kimsenin sınırı olamaz.

100018011602170326047558560 (100)1 (80)yigin

i = 3 (fiyat 70): üstteki 1. gün (80) daha yüksek → span = 3 − 1 = 2.

100018011602170326047558560 (100)1 (80)3 (70)yigin

push(3): sonraki günler için olası sınır.

1000180116021703260417558560 (100)1 (80)3 (70)yigin

i = 4 (fiyat 60): üstteki 3. gün (70) daha yüksek → span = 4 − 3 = 1.

1000180116021703260417558560 (100)1 (80)3 (70)4 (60)yigin

push(4): sonraki günler için olası sınır.

1000180116021703260417558560 (100)1 (80)3 (70)4 (60)yigin

i = 5: 75 ≥ fiyatlar[4] = 60 → pop. 4. gün artık kimsenin sınırı olamaz.

1000180116021703260417558560 (100)1 (80)3 (70)yigin

i = 5: 75 ≥ fiyatlar[3] = 70 → pop. 3. gün artık kimsenin sınırı olamaz.

10001801160217032604175548560 (100)1 (80)yigin

i = 5 (fiyat 75): üstteki 1. gün (80) daha yüksek → span = 5 − 1 = 4.

10001801160217032604175548560 (100)1 (80)5 (75)yigin

push(5): sonraki günler için olası sınır.

10001801160217032604175548560 (100)1 (80)5 (75)yigin

i = 6: 85 ≥ fiyatlar[5] = 75 → pop. 5. gün artık kimsenin sınırı olamaz.

10001801160217032604175548560 (100)1 (80)yigin

i = 6: 85 ≥ fiyatlar[1] = 80 → pop. 1. gün artık kimsenin sınırı olamaz.

100018011602170326041755485660 (100)yigin

i = 6 (fiyat 85): üstteki 0. gün (100) daha yüksek → span = 6 − 0 = 6.

100018011602170326041755485660 (100)6 (85)yigin

push(6): sonraki günler için olası sınır.

100018011602170326041755485660 (100)6 (85)yigin

return [1, 1, 1, 2, 1, 4, 6]. Her indis bir kez eklenir, en çok bir kez çıkar → O(n).

int[] span(int[] fiyatlar) {
    int[] sicrama = new int[fiyatlar.length];
    Deque<Integer> yigin = new ArrayDeque<>();
    yigin.push(0); sicrama[0] = 1;
    for (int i = 1; i < fiyatlar.length; i++) {
        while (!yigin.isEmpty() &&
            fiyatlar[i] >= fiyatlar[yigin.peek()])
            yigin.pop();
        sicrama[i] = yigin.isEmpty() ? i + 1
                              : i - yigin.peek();
        yigin.push(i);
    }
    return sicrama;
}

İfade Gösterimleri

Infix, Prefix ve Postfix Gösterimleri

GösterimOperatörün yeriA * (B + C) / D
Ara ek (infix)işlenenlerin arasındaA * ( B + C ) / D
Önek (prefix) · Polonya gösterimiişlenenlerden önce/ * A + B C D
Sonek (postfix) · ters Polonya (RPN)işlenenlerden sonraA B C + * D /
  • Prefix ve postfixte parantez ve öncelik kuralı gerekmez: ifade soldan sağa (ya da sağdan sola) tek geçişte, yığınla değerlendirilir.
  • Derleyiciler ve yorumlayıcılar ifadeleri çoğunlukla postfix biçimine (ör. yığın tabanlı bayt koduna) çevirir; hesap makineleri de RPN kullanabilir.
  • Sembolik cebir ve ifade ağaçları üzerinde çalışırken de kullanılır.
Dikkat: prefix * A / + B C D yanlıştır; bu A * ((B + C) / D) demektir. Soldan sağa işlem sırasına uygun doğru prefix: / * A + B C D.

İfade Gösterimleri

Öncelik, Birleşme ve Elle Dönüşüm

OperatörÖncelikBirleşme
^ (üs)3sağdan sola
* /2soldan sağa
+ −1soldan sağa

Aynı öncelikte soldan sağa: A − B + C = (A − B) + C → postfix A B − C +.

Elle dönüşüm: tam parantezleme
  1. Her işlemi parantezle: ((A * (B + C)) / D)
  2. Postfix: her operatörü kendi parantezinin sağına taşı → A B C + * D /
  3. Prefix: her operatörü kendi parantezinin soluna taşı → / * A + B C D
İşlenenlerin (A, B, C, D) sırası üç gösterimde de aynı kalır; yalnızca operatörlerin yeri değişir. Bu, dönüşümü kontrol etmenin hızlı bir yoludur.

İfade Gösterimleri

Infix → Postfix: Algoritma

Girdi soldan sağa okunur; operatörler ve açık parantezler bir yığında bekletilir.

Okunan simgeYapılacak işlem
İşlenen (A, B, 5 …)Doğrudan çıktıya yaz.
(Yığına koy.
)( görülene kadar yığından çıkarıp çıktıya yaz; ('ı at.
Operatör opÜstte ( olmadığı ve üstteki operatörün önceliği ≥ op olduğu sürece çıkarıp çıktıya yaz; sonra op'u yığına koy.
Girdi bittiYığında kalan tüm operatörleri çıkarıp çıktıya yaz.

“≥” kuralı soldan sağa birleşmeyi sağlar. Sağdan birleşen ^ için yalnızca önceliği > olanlar çıkarılır. Zaman O(n).

İfade Gösterimleri

Infix → Postfix: Java

String infixToPostfix(String ifade) {
  StringBuilder cikti = new StringBuilder();
  Deque<Character> yigin = new ArrayDeque<>();
  for (char c : ifade.toCharArray()) {
    if (Character.isLetterOrDigit(c)) cikti.append(c);
    else if (c == '(') yigin.push(c);
    else if (c == ')') {
      while (yigin.peek() != '(')
        cikti.append(yigin.pop());
      yigin.pop();                  // '(' atılır
    } else {                        // operatör
      while (!yigin.isEmpty() &&
          oncelik(yigin.peek()) >= oncelik(c))
        cikti.append(yigin.pop());
      yigin.push(c);
    }
  }
  while (!yigin.isEmpty()) cikti.append(yigin.pop());
  return cikti.toString();
}
int oncelik(char op) {
  switch (op) {
    case '+': case '-': return 1;
    case '*': case '/': return 2;
    default: return 0;   // '('
  }
}
VarsayımlarTek karakterli işlenenler, yalnızca + − * / (^ yok), boşluksuz ve geçerli ifade.
Neden ( → 0?Operatör geldiğinde ( hiçbir zaman çıkarılmaz; yalnızca ) onu atar.

İfade Gösterimleri · Kodu adım adım çalıştır (yalnızca döngü)

Kod İzleme: infixToPostfix("A*(B+C)/D")

ifadeA*(B+C)/Dcikti:boşyigin

Girdi soldan sağa okunacak. İşlenenler çıktıya, operatörler ve '(' yığına gider.

ifadeA*(B+C)/D▲ ccikti:Aboşyigin

A işlenen → doğrudan çıktıya.

ifadeA*(B+C)/D▲ ccikti:A*yigin

*: yığın boş → push *.

ifadeA*(B+C)/D▲ ccikti:A*(yigin

( → her zaman yığına. Kapanana kadar içindeki operatörleri korur.

ifadeA*(B+C)/D▲ ccikti:AB*(yigin

B işlenen → doğrudan çıktıya.

ifadeA*(B+C)/D▲ ccikti:AB*(+yigin

+: üstte '(' var → push +.

ifadeA*(B+C)/D▲ ccikti:ABC*(+yigin

C işlenen → doğrudan çıktıya.

ifadeA*(B+C)/D▲ ccikti:ABC+*(+yigin

): '(' görülene kadar pop → + çıktıya.

ifadeA*(B+C)/D▲ ccikti:ABC+*(yigin

'(' yığından atılır; parantezler çıktıya yazılmaz.

ifadeA*(B+C)/D▲ ccikti:ABC+**yigin

/: üstteki * önceliği (2) = / önceliği (2) → * çıktıya.

ifadeA*(B+C)/D▲ ccikti:ABC+*/yigin

/: yığın boş → push /.

ifadeA*(B+C)/D▲ ccikti:ABC+*D/yigin

D işlenen → doğrudan çıktıya.

ifadeA*(B+C)/Dcikti:ABC+*D//yigin

Girdi bitti: kalan operatör / çıktıya. Sonuç: A B C + * D /

for (char c : ifade.toCharArray()) {
  if (Character.isLetterOrDigit(c)) cikti.append(c);
  else if (c == '(') yigin.push(c);
  else if (c == ')') {
    while (yigin.peek() != '(')
      cikti.append(yigin.pop());
    yigin.pop();                  // '(' atılır
  } else {                        // operatör
    while (!yigin.isEmpty() &&
        oncelik(yigin.peek()) >= oncelik(c))
      cikti.append(yigin.pop());
    yigin.push(c);
  }
}
while (!yigin.isEmpty()) cikti.append(yigin.pop());

İfade Gösterimleri · Kodu adım adım çalıştır (yalnızca döngü)

Kod İzleme: infixToPostfix("A+B*C-D/E")

ifadeA+B*C-D/Ecikti:boşyigin

Girdi soldan sağa okunacak. İşlenenler çıktıya, operatörler ve '(' yığına gider.

ifadeA+B*C-D/E▲ ccikti:Aboşyigin

A işlenen → doğrudan çıktıya.

ifadeA+B*C-D/E▲ ccikti:A+yigin

+: yığın boş → push +.

ifadeA+B*C-D/E▲ ccikti:AB+yigin

B işlenen → doğrudan çıktıya.

ifadeA+B*C-D/E▲ ccikti:AB+*yigin

*: üstteki + önceliği (1) < 2 → push *.

ifadeA+B*C-D/E▲ ccikti:ABC+*yigin

C işlenen → doğrudan çıktıya.

ifadeA+B*C-D/E▲ ccikti:ABC*+*yigin

-: üstteki * önceliği (2) > - önceliği (1) → * çıktıya.

ifadeA+B*C-D/E▲ ccikti:ABC*++yigin

-: üstteki + önceliği (1) = - önceliği (1) → + çıktıya.

ifadeA+B*C-D/E▲ ccikti:ABC*+-yigin

-: yığın boş → push -.

ifadeA+B*C-D/E▲ ccikti:ABC*+D-yigin

D işlenen → doğrudan çıktıya.

ifadeA+B*C-D/E▲ ccikti:ABC*+D-/yigin

/: üstteki - önceliği (1) < 2 → push /.

ifadeA+B*C-D/E▲ ccikti:ABC*+DE-/yigin

E işlenen → doğrudan çıktıya.

ifadeA+B*C-D/Ecikti:ABC*+DE/-/yigin

Girdi bitti: kalan operatör / çıktıya.

ifadeA+B*C-D/Ecikti:ABC*+DE/--yigin

Girdi bitti: kalan operatör - çıktıya. Sonuç: A B C * + D E / − = (A + B·C) − D/E

for (char c : ifade.toCharArray()) {
  if (Character.isLetterOrDigit(c)) cikti.append(c);
  else if (c == '(') yigin.push(c);
  else if (c == ')') {
    while (yigin.peek() != '(')
      cikti.append(yigin.pop());
    yigin.pop();                  // '(' atılır
  } else {                        // operatör
    while (!yigin.isEmpty() &&
        oncelik(yigin.peek()) >= oncelik(c))
      cikti.append(yigin.pop());
    yigin.push(c);
  }
}
while (!yigin.isEmpty()) cikti.append(yigin.pop());

İfade Gösterimleri

Postfix İfadeyi Değerlendirme

  • Soldan sağa oku.
  • Sayı → yığına push.
  • Operatör → iki öğe pop: önce sağ, sonra sol işlenen; sonucu push.
  • Sonda yığında tek öğe kalır: sonuç. Parantez ve öncelik yok → O(n).
int hesapla(int sol, char op, int sag) {
  switch (op) {
    case '+': return sol + sag;
    case '-': return sol - sag;
    case '*': return sol * sag;
    default:  return sol / sag;
  }
}
int postfixDegerlendir(String ifade) {
  Deque<Integer> yigin = new ArrayDeque<>();
  for (char c : ifade.toCharArray()) {
    if (Character.isDigit(c)) {
      yigin.push(c - '0');
    } else {
      int sag = yigin.pop();
      int sol = yigin.pop();
      yigin.push(hesapla(sol, c, sag));
    }
  }
  return yigin.pop();
}

Varsayım: tek basamaklı sayılar, boşluksuz ve geçerli ifade.

İfade Gösterimleri · Kodu adım adım çalıştır

Kod İzleme: postfixDegerlendir("412+*3/")

ifade412+*3/boşyigin

A=4, B=1, C=2, D=3 için A B C + * D / → "412+*3/". Boş işlenen yığını oluşturulur.

ifade412+*3/▲ c4yigin

4 rakam → c - '0' = 4 yığına.

ifade412+*3/▲ c41yigin

1 rakam → c - '0' = 1 yığına.

ifade412+*3/▲ c412yigin

2 rakam → c - '0' = 2 yığına.

ifade412+*3/▲ c2sag1sol4yigin

+ operatör: önce sag = 2, sonra sol = 1 çıkar (sıra önemli: − ve / için).

ifade412+*3/▲ c1 + 2 = 343yigin

hesapla(1, '+', 2) = 3 → sonuç yığına geri konur.

ifade412+*3/▲ c3sag4solboşyigin

* operatör: önce sag = 3, sonra sol = 4 çıkar (sıra önemli: − ve / için).

ifade412+*3/▲ c4 * 3 = 1212yigin

hesapla(4, '*', 3) = 12 → sonuç yığına geri konur.

ifade412+*3/▲ c123yigin

3 rakam → c - '0' = 3 yığına.

ifade412+*3/▲ c3sag12solboşyigin

/ operatör: önce sag = 3, sonra sol = 12 çıkar (sıra önemli: − ve / için).

ifade412+*3/▲ c12 / 3 = 44yigin

hesapla(12, '/', 3) = 4 → sonuç yığına geri konur.

ifade412+*3/sonuç = 4boşyigin

İfade bitti; yığındaki tek değer sonuçtur: return 4. Parantez ve öncelik gerekmedi → O(n).

int postfixDegerlendir(String ifade) {
  Deque<Integer> yigin = new ArrayDeque<>();
  for (char c : ifade.toCharArray()) {
    if (Character.isDigit(c)) {
      yigin.push(c - '0');
    } else {
      int sag = yigin.pop();
      int sol = yigin.pop();
      yigin.push(hesapla(sol, c, sag));
    }
  }
  return yigin.pop();
}

İfade Gösterimleri

Infix → Prefix Dönüşümü

  1. Infix ifadeyi ters çevir.
  2. Parantezleri değiştir: ( ↔ ).
  3. Postfix algoritmasını uygula; ama eşit öncelikte çıkarma, yalnızca önceliği > olanları çıkar.
  4. Çıktıyı ters çevir.
AdımA * (B + C) / D
1–2. Ters çevir, parantezleri değiştirD / ( C + B ) * A
3. Postfix (eşitte çıkarmadan)D C B + A * /
4. Ters çevir → prefix/ * A + B C D
3. adımda * geldiğinde üstte eşit öncelikli / vardır; / çıkarılmaz. Böylece ters çevirince soldan birleşme korunur: önce A * (B + C), sonra / D.
ÖrnekA + B * C − D / E → − + A * B C / D E
Prefix değerlendirmePostfix gibi, ama ifade sağdan sola okunur; ilk çıkan sol işlenendir.

İfade Gösterimleri · Kodu adım adım çalıştır

Kod İzleme: infixToPrefix("A*(B+C)/D")

ifadeA*(B+C)/D

main, infixToPrefix("A*(B+C)/D") çağırır.

ifadeA*(B+C)/Dt = ters(ifade)D/)C+B(*A

1. Dize ters çevrilir. Parantezler de ters döndü: ')' solda, '(' sağda.

ifadeA*(B+C)/Dt = ters(ifade)D/)C+B(*At = parantezCevir(t)D/(C+B)*A

2. ( ↔ ) değiştirilir; artık geçerli bir infix ifade: D / (C + B) * A.

ifadeA*(B+C)/Dt = ters(ifade)D/)C+B(*At = parantezCevir(t)D/(C+B)*Ap (eşitte pop yok)DCB+A*/

3. Postfix algoritması, ama eşit öncelikte pop yapmadan → p = D C B + A * /.

ifadeA*(B+C)/Dt = ters(ifade)D/)C+B(*At = parantezCevir(t)D/(C+B)*Ap (eşitte pop yok)DCB+A*/ters(p) → prefix/*A+BCD

4. p ters çevrilir → / * A + B C D döner.

ifadeA*(B+C)/Dt = ters(ifade)D/)C+B(*At = parantezCevir(t)D/(C+B)*Ap (eşitte pop yok)DCB+A*/ters(p) → prefix/*A+BCD

main sonucu yazdırır. Kontrol: işlenenler A, B, C, D sırası korunmuş.

String ters(String s) {
  return new StringBuilder(s).reverse().toString();
}
String infixToPrefix(String ifade) {
  String t = ters(ifade);          // 1. ters çevir
  t = parantezCevir(t);            // 2. ( ↔ )
  String p = prefixIcinPostfix(t); // 3. eşitte pop yok
  return ters(p);                  // 4. ters çevir
}
// main içinde:
System.out.println(infixToPrefix("A*(B+C)/D"));

İfade Gösterimleri · Kodu adım adım çalıştır (yalnızca döngü)

Kod İzleme: prefixIcinPostfix("D/(C+B)*A")

t (ters çevrilmiş, parantezler değişmiş)D/(C+B)*Acikti:boşyigin

Girdi soldan sağa okunacak. İşlenenler çıktıya, operatörler ve '(' yığına gider.

t (ters çevrilmiş, parantezler değişmiş)D/(C+B)*A▲ ccikti:Dboşyigin

D işlenen → doğrudan çıktıya.

t (ters çevrilmiş, parantezler değişmiş)D/(C+B)*A▲ ccikti:D/yigin

/: yığın boş → push /.

t (ters çevrilmiş, parantezler değişmiş)D/(C+B)*A▲ ccikti:D/(yigin

( → her zaman yığına. Kapanana kadar içindeki operatörleri korur.

t (ters çevrilmiş, parantezler değişmiş)D/(C+B)*A▲ ccikti:DC/(yigin

C işlenen → doğrudan çıktıya.

t (ters çevrilmiş, parantezler değişmiş)D/(C+B)*A▲ ccikti:DC/(+yigin

+: üstte '(' var → push +.

t (ters çevrilmiş, parantezler değişmiş)D/(C+B)*A▲ ccikti:DCB/(+yigin

B işlenen → doğrudan çıktıya.

t (ters çevrilmiş, parantezler değişmiş)D/(C+B)*A▲ ccikti:DCB+/(+yigin

): '(' görülene kadar pop → + çıktıya.

t (ters çevrilmiş, parantezler değişmiş)D/(C+B)*A▲ ccikti:DCB+/(yigin

'(' yığından atılır; parantezler çıktıya yazılmaz.

t (ters çevrilmiş, parantezler değişmiş)D/(C+B)*A▲ ccikti:DCB+/*yigin

*: üstteki / önceliği (2) = 2, eşitte çıkarmıyoruz → push *.

t (ters çevrilmiş, parantezler değişmiş)D/(C+B)*A▲ ccikti:DCB+A/*yigin

A işlenen → doğrudan çıktıya.

t (ters çevrilmiş, parantezler değişmiş)D/(C+B)*Acikti:DCB+A*/*yigin

Girdi bitti: kalan operatör * çıktıya.

t (ters çevrilmiş, parantezler değişmiş)D/(C+B)*Acikti:DCB+A*//yigin

Girdi bitti: kalan operatör / çıktıya. p = D C B + A * / → ters: / * A + B C D

for (char c : ifade.toCharArray()) {
  if (Character.isLetterOrDigit(c)) cikti.append(c);
  else if (c == '(') yigin.push(c);
  else if (c == ')') {
    while (yigin.peek() != '(')
      cikti.append(yigin.pop());
    yigin.pop();                  // '(' atılır
  } else {                        // operatör
    while (!yigin.isEmpty() &&
        oncelik(yigin.peek()) > oncelik(c))
      cikti.append(yigin.pop());
    yigin.push(c);
  }
}
while (!yigin.isEmpty()) cikti.append(yigin.pop());

Özet

Sık Yapılan Hatalar

Boş yığında pop / peekÖnce isEmpty(); yoksa istisna ya da null (kütüphaneye göre). Parantez kontrolünde "())" tam bu hatayı tetikler.
top ile size karışıklığıDizi yığınında top son öğenin indisidir, size = top + 1. Dolu koşulu top == kapasite − 1.
Çıkarılan hücreyi boşaltmamakNesne dizisinde veri[top--] = null yapılmazsa nesne çöp toplayıcıdan kaçar (bellek sızıntısı, loitering).
Postfix'te işlenen sırasıİlk pop sağ, ikinci pop sol işlenendir: 5 2 − = 5 − 2 = 3, 2 − 5 değil.
Yineleme sırasıArrayDeque for-each ile üstten alta, eski Stack ise alttan üste gezinir; çıktılar ters görünür.
Sonsuz özyinelemeTaban durum eksikse çerçeveler birikir ve StackOverflowError oluşur (çağrı yığını taşar).

Özet

Özet: Yığınla Çözülen Problemler

ProblemYığında tutulanZaman
push / pop / top / isEmpty / size—O(1)
Dize ters çevirmekarakterlerO(n)
Parantez kontrolüeşleşmemiş açık parantezlerO(n)
Sonraki büyük öğesağdaki aday değerler (azalan)O(n)
Stock spanhenüz aşılmamış günlerin indisleriO(n)
Hanoi kulelerialt görevler (n, kaynak, yard., hedef)O(2n)
Infix → postfix / prefixoperatörler ve (O(n)
Postfix değerlendirmeara sonuçlarO(n)
Min yığın (getMin)her an en küçük değerO(1)
Geri / ileri gezinmeziyaret edilen sayfalar (iki yığın)O(1)

Ortak desen: “en son görülen, henüz sonuçlanmamış öğe” ile ilgileniyorsak yığın kullanırız (LIFO).

Özet

Etkileşimli Simülatörler

Yığın (Stack)push, pop, peek işlemleri; dizi ve bağlı liste gösterimi.
Stock SpanFiyat dizisi işlenirken yığının nasıl değiştiği.
Infix → PostfixOperatör yığını ve çıktı, simge simge.
Infix → PrefixTers çevirme yöntemi, adım adım.

Özet

Örnek Problemler

  1. Boş yığına sırayla push(5), push(3), pop(), push(8), push(1), pop(), pop(), push(4) uygulanıyor. pop()'ların döndürdüğü değerler ve son yığın nedir?
  2. {[()()]} ve {[(])} dizilerinden hangisi geçerlidir? Geçersiz olanda hata hangi karakterde fark edilir?
  3. (A + B) * (C − D) / E ifadesini postfix ve prefix biçimine çevirin.
  4. 5 1 2 + 4 * + 3 − postfix ifadesinin değeri nedir?
  5. Fiyatlar [10, 4, 5, 90, 120, 80] için span dizisini bulun.
  6. {2, 5, 3, 1, 4} dizisi için sonraki büyük öğe dizisini bulun.
Cevapları göster
  1. pop'lar: 3, 1, 8. Son yığın (alttan üste): 5, 4.
  2. {[()()]} geçerli. {[(])}: ] geldiğinde üstte ( var → geçersiz.
  3. Postfix: A B + C D − * E / · Prefix: / * + A B − C D E
  4. 1+2 = 3, 3·4 = 12, 5+12 = 17, 17−3 = 14.
  5. [1, 1, 2, 4, 5, 1]
  6. {5, −1, 4, 4, −1}.

Özet

Alıştırmalar

  1. 1, 2, 3, 4 sırayla push ediliyor, araya pop'lar serpiştirilebiliyor. 2 4 3 1 ve 4 1 2 3 çıkış sıraları mümkün mü?
  2. Tek dizide iki yığın (biri baştan, biri sondan büyür): taşma koşulu nedir?
  3. Tek bir yardımcı yığınla bir yığını sıralayın. Karmaşıklık?
  4. Bir kelimenin palindrom olduğunu yığınla kontrol edin.
  5. <b><i>…</i></b> gibi HTML etiketlerinin iç içeliği nasıl denetlenir?
  6. Dizi dolunca 2 katı yerine +10 büyütülseydi n push'un toplam maliyeti?
İpuçları / cevaplar
  1. 2 4 3 1 evet; 4 1 2 3 hayır (4 çıkınca üstte 3 var).
  2. top1 = −1, top2 = N'den başlar; dolu: top1 + 1 == top2.
  3. x'i al; yardımcıda x'ten küçükleri geri taşı, x'i push et → O(n²).
  4. Harfleri push et, pop ile ters dizeyi kurup karşılaştır → O(n).
  5. Parantez algoritması: açılış etiketi push, kapanışta pop + ad eşleşmesi.
  6. Kopyalar 10 + 20 + 30 + … ≈ n²/20 → O(n²); push başına O(n).

Özet

Kaynaklar

  • Goodrich, Tamassia ve Goldwasser, Data Structures and Algorithms in Java, 6. baskı, 2014.
  • Lafore, Data Structures and Algorithms in Java, 2. baskı, 2002.
  • Oracle, Java SE 17 Deque ve ArrayDeque API belgeleri.

Son

Bölüm 7: Yığınlar
Sonraki sunum: Bölüm 8: Kuyruklar ve Deque · Çalışma soruları · Simülatörler: Stack Simulator, Stock Span Simulator, Infix to Prefix Converter, Infix to Postfix Converter · Terim sözlüğü
1 / 1 Sercan KÜLCÜ, Tüm hakları saklıdır.