Bölüm 7: Yığınlar
Giriş
Öğrenme Çıktıları
Yığının son giren ilk çıkar davranışını açıklamak.
Yığını dizi ve bağlı liste kullanarak gerçekleştirmek.
Yığınları ifade çözümleme ve çağrı yönetiminde kullanmak.
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.
Yığın ADT
Temel İşlemler
| İşlem | Açı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. |
Yığın ADT
push ve pop Adım Adım
Başlangıçta yığın boş: size = 0, üstte öğe yok.
push(10): 10 boş yığına eklenir; üst (top) artık 10.
push(15): 15, 10'un üstüne konur.
push(20): 20 en üste konur; size = 3.
top(): üstteki 20 okunur ama çıkarılmaz.
pop(): son giren 20 ilk çıkar (LIFO).
pop(): 15 çıkar; üst artık 10.
pop(): 10 çıkar; yığın yeniden boş.
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ığında | pop() | top() / peek() |
|---|---|---|
Kendi Stack<E>'imiz (yukarıdaki) | istisna fırlatır | null döner |
java.util.Stack | EmptyStackException | EmptyStackException |
java.util.ArrayDeque | NoSuchElementException | null 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.
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
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
başla eğer yığın boşsa null döndür değilse yığın[top] döndür son
isEmpty: top = −1 ise true, değilse false size: top + 1 döndür
Yığın ADT
Zaman ve Alan Karmaşıklığı
| İşlem | Zaman | Neden? |
|---|---|---|
push | O(1) | yalnızca üst değişir |
pop | O(1) | yalnızca üst değişir |
top | O(1) | üst doğrudan bilinir |
isEmpty, size | O(1) | sayaç / indis tutulur |
| bir öğeyi arama | O(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).
Gerçekleme
Yığın Türleri
- 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.
- Ç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.
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.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.
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
nullatanmalı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
new DiziYigin<>(4): 4 hücrelik dizi, hepsi null; boş yığında top = −1.
push(10): dolu mu? top = -1, veri.length − 1 = 3 → hayır, devam.
veri[++top] = x: önce top -1 → 0, sonra veri[0] = 10. Tek atama → O(1).
push(15): dolu mu? top = 0, veri.length − 1 = 3 → hayır, devam.
veri[++top] = x: önce top 0 → 1, sonra veri[1] = 15. Tek atama → O(1).
push(20): dolu mu? top = 1, veri.length − 1 = 3 → hayır, devam.
veri[++top] = x: önce top 1 → 2, sonra veri[2] = 20. Tek atama → O(1).
top(): yığın boş değil → veri[2] = 20 döner. Dizi ve top değişmez.
pop(): isEmpty()? top = 2 ≠ −1 → boş değil, devam.
E x = veri[top] → x = 20 (üstteki öğe saklanır).
veri[top--] = null: hücre boşaltılır (nesne çöp toplayıcıya bırakılır), top → 1.
return 20: son giren ilk çıktı (LIFO).
push(25): dolu mu? top = 1, veri.length − 1 = 3 → hayır, devam.
veri[++top] = x: önce top 1 → 2, sonra veri[2] = 25. Tek atama → O(1).
push(30): dolu mu? top = 2, veri.length − 1 = 3 → hayır, devam.
veri[++top] = x: önce top 2 → 3, sonra veri[3] = 30. Tek atama → O(1).
push(40): dolu mu? top = 3, veri.length − 1 = 3 → evet, dolu!
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.
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
Başlangıç: top = null, uzunluk = 0 → boş yığın.
push(10): new Dugum(10) oluşturulur; gecici onu gösterir, sonraki alanı varsayılan olarak null.
gecici.sonraki = top: top null olduğu için sonraki yine null (yeni düğüm tek başına).
top = gecici: yığının üstü artık yeni düğüm.
uzunluk++ → 1. Metot biter; yerel gecici kaybolur, düğüm top üzerinden erişilir.
push(15): yeni düğüm 15 oluşturulur. Henüz listeye bağlı değil; top hâlâ 10'u gösterir.
gecici.sonraki = top: 15, eski üst olan 10'a bağlanır. Önce bağlarız ki eski liste kaybolmasın.
top = gecici: yeni üst 15. Listenin başına ekleme, kaydırma yok → O(1).
uzunluk++ → 2.
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
Yığında 15 ve 10 var. Döngü pop()'u 3 kez çağıracak.
pop(): önce boşluk kontrolü; top null değil → devam.
sonuc = top.veri → 15 saklanır (düğüm kopmadan önce).
top = top.sonraki: top bir aşağı iner → düğüm(10). Eski üst düğüme artık referans yok.
uzunluk-- → 1.
return 15. Erişilemeyen düğümü çöp toplayıcı temizler.
main, dönen 15 değerini yazdırır.
pop(): önce boşluk kontrolü; top null değil → devam.
sonuc = top.veri → 10 saklanır (düğüm kopmadan önce).
top = top.sonraki: top bir aşağı iner → null. Eski üst düğüme artık referans yok.
uzunluk-- → 0.
return 10. Erişilemeyen düğümü çöp toplayıcı temizler.
main, dönen 10 değerini yazdırır.
pop(): top == null → yığın boş!
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çüt | Dizi tabanlı | Bağlı liste tabanlı |
|---|---|---|
| Boyut | sabit kapasite (dinamik dizide büyütülür) | esnek, çalışma zamanında değişir |
| push / pop | O(1) (büyütmede amortize O(1)) | O(1) |
| Taşma | taşma mümkün | yalnızca bellek biterse |
| Öğe başına bellek | yalnızca veri | veri + sonraki referansı |
| Boş yer | kullanılmayan kapasite | yok |
| Java | ArrayDeque, Stack (Vector tabanlı) | LinkedList (Deque olarak) |
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
Yığın Uygulamaları
Tarayıcıda Geri ve İleri: İki Yığın
Tarayıcı açıldı: şu anki sayfa Ana sayfa; iki yığın da boş.
ziyaret(Haberler): eski sayfa geri yığınına push, yeni sayfa açılır.
ziyaret(Spor): eski sayfa geri yığınına push, yeni sayfa açılır.
geri(): şu anki sayfa ileri yığınına push; geri yığınından pop → Haberler.
geri(): şu anki sayfa ileri yığınına push; geri yığınından pop → Ana sayfa.
ileri(): şu anki sayfa geri yığınına push; ileri yığınından pop → Haberler.
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(): şu anki sayfa ileri yığınına push; geri yığınından pop → Haberler.
Yığın Uygulamaları · Kodu adım adım çalıştır
Çağrı Yığını: fakt(3) Adım Adım
main çalışıyor; fakt(3) çağrılacak. Her çağrı yığına yeni bir çerçeve (frame) iter.
fakt(3) çerçevesi yığına push edildi. n <= 1? 3 <= 1 → false.
3 * fakt(2) hesaplanamaz: önce fakt(2) bitmeli. Bu çerçeve askıda bekler.
fakt(2) çerçevesi yığına push edildi. n <= 1? 2 <= 1 → false.
2 * fakt(1) hesaplanamaz: önce fakt(1) bitmeli. Bu çerçeve askıda bekler.
fakt(1) çerçevesi yığına push edildi. n <= 1? 1 <= 1 → true.
Taban durum: return 1. Artık yığın çözülmeye (unwinding) başlar.
fakt(1) pop edildi, 1 döndü. fakt(2) devam eder: 2 * 1 = 2.
fakt(2) pop edildi, 2 döndü. fakt(3) devam eder: 3 * 2 = 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
main, tersCevir("ABCD") çağırır.
Boş bir karakter yığını oluşturulur.
Dize karakter dizisine kopyalanır (üzerine yazacağız).
1. döngü: c = 'A' → push('A').
1. döngü: c = 'B' → push('B').
1. döngü: c = 'C' → push('C').
1. döngü: c = 'D' → push('D'). Tüm karakterler yığında; en üstte son karakter.
2. döngü, i = 0: pop() → 'D', karakterler[0] = 'D'. Çıkış sırası girişin tersi.
2. döngü, i = 1: pop() → 'C', karakterler[1] = 'C'. Çıkış sırası girişin tersi.
2. döngü, i = 2: pop() → 'B', karakterler[2] = 'B'. Çıkış sırası girişin tersi.
2. döngü, i = 3: pop() → 'A', karakterler[3] = 'A'. Çıkış sırası girişin tersi.
return "DCBA". Zaman ve ek bellek O(n).
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:
- her açık parantez aynı tip bir parantezle kapatılmışsa ve
- parantezler doğru sırada kapatılmışsa (en son açılan ilk kapanır → LIFO!).
- 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).
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(); }
boolean eslesir(char a, char k) { return (a == '(' && k == ')') || (a == '[' && k == ']') || (a == '{' && k == '}'); }
"())" ya da "]" girdisinde boş yığından pop → istisna.Yığın Uygulamaları · Kodu adım adım çalıştır
Kod İzleme: gecerliMi("{{[]}()}[]")
Eşleşmemiş açık parantezleri tutacak boş yığın oluşturulur.
'{' açık parantez → push. Kapanışını bekleyecek.
'{' açık parantez → push. Kapanışını bekleyecek.
'[' açık parantez → push. Kapanışını bekleyecek.
']' kapalı parantez: yığın boş değil → pop, en son açılan '[' çıkar.
eslesir('[', ']') → true: aynı tip, doğru sıra. Devam.
'}' kapalı parantez: yığın boş değil → pop, en son açılan '{' çıkar.
eslesir('{', '}') → true: aynı tip, doğru sıra. Devam.
'(' açık parantez → push. Kapanışını bekleyecek.
')' kapalı parantez: yığın boş değil → pop, en son açılan '(' çıkar.
eslesir('(', ')') → true: aynı tip, doğru sıra. Devam.
'}' kapalı parantez: yığın boş değil → pop, en son açılan '{' çıkar.
eslesir('{', '}') → true: aynı tip, doğru sıra. Devam.
'[' açık parantez → push. Kapanışını bekleyecek.
']' kapalı parantez: yığın boş değil → pop, en son açılan '[' çıkar.
eslesir('[', ']') → true: aynı tip, doğru sıra. Devam.
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("([)]")
Eşleşmemiş açık parantezleri tutacak boş yığın oluşturulur.
'(' açık parantez → push. Kapanışını bekleyecek.
'[' açık parantez → push. Kapanışını bekleyecek.
')' kapalı parantez: yığın boş değil → pop, en son açılan '[' çıkar.
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.
| dizi | 4 | 7 | 3 | 4 | 8 | 1 |
|---|---|---|---|---|---|---|
| sonuc | 7 | 8 | 4 | 8 | −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})
sonuc (hepsi 0) ve boş yığın oluşturulur. Dizi sağdan sola gezilecek.
i = 5 (dizi[i] = 1): yığın boş → −1.
push(1): soldaki öğeler için yeni aday. Yığın alttan üste azalan kalır.
i = 4: üstteki 1 ≤ 8 → pop. 1, 8'in solundakiler için asla cevap olamaz (arada daha büyük 8 var).
i = 4 (dizi[i] = 8): yığın boş → −1.
push(8): soldaki öğeler için yeni aday. Yığın alttan üste azalan kalır.
i = 3 (dizi[i] = 4): üstteki 8 > 4 → sonuc[3] = 8.
push(4): soldaki öğeler için yeni aday. Yığın alttan üste azalan kalır.
i = 2 (dizi[i] = 3): üstteki 4 > 3 → sonuc[2] = 4.
push(3): soldaki öğeler için yeni aday. Yığın alttan üste azalan kalır.
i = 1: üstteki 3 ≤ 7 → pop. 3, 7'nin solundakiler için asla cevap olamaz (arada daha büyük 7 var).
i = 1: üstteki 4 ≤ 7 → pop. 4, 7'nin solundakiler için asla cevap olamaz (arada daha büyük 7 var).
i = 1 (dizi[i] = 7): üstteki 8 > 7 → sonuc[1] = 8.
push(7): soldaki öğeler için yeni aday. Yığın alttan üste azalan kalır.
i = 0 (dizi[i] = 4): üstteki 7 > 4 → sonuc[0] = 7.
push(4): soldaki öğeler için yeni aday. Yığın alttan üste azalan kalır.
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
İki yığın: y tüm öğeleri, min ise her an en küçüğü üstte tutar. Amaç: getMin() O(1).
push(5): y.push(5).
min boş → min.push(5): yeni (ya da eşit) en küçük.
push(3): y.push(3).
3 <= 5 → true → min.push(3): yeni (ya da eşit) en küçük.
push(7): y.push(7).
7 <= 3 → false: 7 en küçük değil, min yığını değişmez.
push(3): y.push(3).
3 <= 3 → true → min.push(3): yeni (ya da eşit) en küçük.
getMin(): min.peek() = 3. Arama yok → O(1).
pop(): y'den 3 çıktı. 3 == min.peek()? evet → min'den de pop; yeni en küçük 3.
getMin(): min.peek() = 3. Arama yok → O(1).
pop(): y'den 7 çıktı. 7 == min.peek()? hayır → min değişmez.
pop(): y'den 3 çıktı. 3 == min.peek()? evet → min'den de pop; yeni en küçük 5.
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())); } } }
Hanoi Kuleleri · Kodu adım adım çalıştır
Kod İzleme: hanoi(3) (yığınla)
Başlangıç görevi (3, A, B, C): “3 diski A'dan C'ye, B yardımcı ile taşı”.
pop → g = (3,A,B,C): şimdi bu görev yapılacak.
n = 3 > 1 → üç alt görev ters sırayla push edilir; ilk yapılacak (2, A, C, B) en üstte kalır (LIFO).
pop → g = (2,A,C,B): şimdi bu görev yapılacak.
n = 2 > 1 → üç alt görev ters sırayla push edilir; ilk yapılacak (1, A, B, C) en üstte kalır (LIFO).
pop → g = (1,A,B,C): şimdi bu görev yapılacak.
n = 1 → doğrudan taşı: disk 1, A → C (hamle 1).
pop → g = (1,A,C,B): şimdi bu görev yapılacak.
n = 1 → doğrudan taşı: disk 2, A → B (hamle 2).
pop → g = (1,C,A,B): şimdi bu görev yapılacak.
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)
pop → g = (1,A,B,C): şimdi bu görev yapılacak.
n = 1 → doğrudan taşı: disk 3, A → C (hamle 4).
pop → g = (2,B,A,C): şimdi bu görev yapılacak.
n = 2 > 1 → üç alt görev ters sırayla push edilir; ilk yapılacak (1, B, C, A) en üstte kalır (LIFO).
pop → g = (1,B,C,A): şimdi bu görev yapılacak.
n = 1 → doğrudan taşı: disk 1, B → A (hamle 5).
pop → g = (1,B,A,C): şimdi bu görev yapılacak.
n = 1 → doğrudan taşı: disk 2, B → C (hamle 6).
pop → g = (1,A,B,C): şimdi bu görev yapılacak.
n = 1 → doğrudan taşı: disk 1, A → C (hamle 7).
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ün | Fiyat | Span | Neden? |
|---|---|---|---|
| 1 | 100 | 1 | ilk gün |
| 4 | 70 | 2 | 60 ≤ 70, 80 > 70 |
| 6 | 75 | 4 | 60, 70, 60 ≤ 75; 80 > 75 |
| 7 | 85 | 6 | 75, 60, 70, 60, 80 ≤ 85 |
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; }
Stock Span · Kodu adım adım çalıştır
Kod İzleme: span({100, 80, 60, 70, 60, 75, 85})
İlk gün: indis 0 yığına, sicrama[0] = 1 (öncesinde gün yok).
i = 1 (fiyat 80): üstteki 0. gün (100) daha yüksek → span = 1 − 0 = 1.
push(1): sonraki günler için olası sınır.
i = 2 (fiyat 60): üstteki 1. gün (80) daha yüksek → span = 2 − 1 = 1.
push(2): sonraki günler için olası sınır.
i = 3: 70 ≥ fiyatlar[2] = 60 → pop. 2. gün artık kimsenin sınırı olamaz.
i = 3 (fiyat 70): üstteki 1. gün (80) daha yüksek → span = 3 − 1 = 2.
push(3): sonraki günler için olası sınır.
i = 4 (fiyat 60): üstteki 3. gün (70) daha yüksek → span = 4 − 3 = 1.
push(4): sonraki günler için olası sınır.
i = 5: 75 ≥ fiyatlar[4] = 60 → pop. 4. gün artık kimsenin sınırı olamaz.
i = 5: 75 ≥ fiyatlar[3] = 70 → pop. 3. gün artık kimsenin sınırı olamaz.
i = 5 (fiyat 75): üstteki 1. gün (80) daha yüksek → span = 5 − 1 = 4.
push(5): sonraki günler için olası sınır.
i = 6: 85 ≥ fiyatlar[5] = 75 → pop. 5. gün artık kimsenin sınırı olamaz.
i = 6: 85 ≥ fiyatlar[1] = 80 → pop. 1. gün artık kimsenin sınırı olamaz.
i = 6 (fiyat 85): üstteki 0. gün (100) daha yüksek → span = 6 − 0 = 6.
push(6): sonraki günler için olası sınır.
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österim | Operatörün yeri | A * (B + C) / D |
|---|---|---|
| Ara ek (infix) | işlenenlerin arasında | A * ( B + C ) / D |
| Önek (prefix) · Polonya gösterimi | işlenenlerden önce | / * A + B C D |
| Sonek (postfix) · ters Polonya (RPN) | işlenenlerden sonra | A 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.
* 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 | Öncelik | Birleşme |
|---|---|---|
^ (üs) | 3 | sağdan sola |
* / | 2 | soldan sağa |
+ − | 1 | soldan sağa |
Aynı öncelikte soldan sağa: A − B + C = (A − B) + C → postfix A B − C +.
- Her işlemi parantezle:
((A * (B + C)) / D) - Postfix: her operatörü kendi parantezinin sağına taşı →
A B C + * D / - Prefix: her operatörü kendi parantezinin soluna taşı →
/ * A + B C D
İfade Gösterimleri
Infix → Postfix: Algoritma
Girdi soldan sağa okunur; operatörler ve açık parantezler bir yığında bekletilir.
| Okunan simge | Yapı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 bitti | Yığı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; // '(' } }
( 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")
Girdi soldan sağa okunacak. İşlenenler çıktıya, operatörler ve '(' yığına gider.
A işlenen → doğrudan çıktıya.
*: yığın boş → push *.
( → her zaman yığına. Kapanana kadar içindeki operatörleri korur.
B işlenen → doğrudan çıktıya.
+: üstte '(' var → push +.
C işlenen → doğrudan çıktıya.
): '(' görülene kadar pop → + çıktıya.
'(' yığından atılır; parantezler çıktıya yazılmaz.
/: üstteki * önceliği (2) = / önceliği (2) → * çıktıya.
/: yığın boş → push /.
D işlenen → doğrudan çıktıya.
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")
Girdi soldan sağa okunacak. İşlenenler çıktıya, operatörler ve '(' yığına gider.
A işlenen → doğrudan çıktıya.
+: yığın boş → push +.
B işlenen → doğrudan çıktıya.
*: üstteki + önceliği (1) < 2 → push *.
C işlenen → doğrudan çıktıya.
-: üstteki * önceliği (2) > - önceliği (1) → * çıktıya.
-: üstteki + önceliği (1) = - önceliği (1) → + çıktıya.
-: yığın boş → push -.
D işlenen → doğrudan çıktıya.
/: üstteki - önceliği (1) < 2 → push /.
E işlenen → doğrudan çıktıya.
Girdi bitti: kalan operatör / çıktıya.
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/")
A=4, B=1, C=2, D=3 için A B C + * D / → "412+*3/". Boş işlenen yığını oluşturulur.
4 rakam → c - '0' = 4 yığına.
1 rakam → c - '0' = 1 yığına.
2 rakam → c - '0' = 2 yığına.
+ operatör: önce sag = 2, sonra sol = 1 çıkar (sıra önemli: − ve / için).
hesapla(1, '+', 2) = 3 → sonuç yığına geri konur.
* operatör: önce sag = 3, sonra sol = 4 çıkar (sıra önemli: − ve / için).
hesapla(4, '*', 3) = 12 → sonuç yığına geri konur.
3 rakam → c - '0' = 3 yığına.
/ operatör: önce sag = 3, sonra sol = 12 çıkar (sıra önemli: − ve / için).
hesapla(12, '/', 3) = 4 → sonuç yığına geri konur.
İ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ü
- Infix ifadeyi ters çevir.
- Parantezleri değiştir:
(↔). - Postfix algoritmasını uygula; ama eşit öncelikte çıkarma, yalnızca önceliği > olanları çıkar.
- Çıktıyı ters çevir.
| Adım | A * (B + C) / D |
|---|---|
| 1–2. Ters çevir, parantezleri değiştir | D / ( C + B ) * A |
| 3. Postfix (eşitte çıkarmadan) | D C B + A * / |
| 4. Ters çevir → prefix | / * A + B C D |
* geldiğinde üstte eşit öncelikli / vardır; / çıkarılmaz. Böylece ters çevirince soldan birleşme korunur: önce A * (B + C), sonra / D.A + B * C − D / E → − + A * B C / D Eİfade Gösterimleri · Kodu adım adım çalıştır
Kod İzleme: infixToPrefix("A*(B+C)/D")
main, infixToPrefix("A*(B+C)/D") çağırır.
1. Dize ters çevrilir. Parantezler de ters döndü: ')' solda, '(' sağda.
2. ( ↔ ) değiştirilir; artık geçerli bir infix ifade: D / (C + B) * A.
3. Postfix algoritması, ama eşit öncelikte pop yapmadan → p = D C B + A * /.
4. p ters çevrilir → / * A + B C D döner.
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")
Girdi soldan sağa okunacak. İşlenenler çıktıya, operatörler ve '(' yığına gider.
D işlenen → doğrudan çıktıya.
/: yığın boş → push /.
( → her zaman yığına. Kapanana kadar içindeki operatörleri korur.
C işlenen → doğrudan çıktıya.
+: üstte '(' var → push +.
B işlenen → doğrudan çıktıya.
): '(' görülene kadar pop → + çıktıya.
'(' yığından atılır; parantezler çıktıya yazılmaz.
*: üstteki / önceliği (2) = 2, eşitte çıkarmıyoruz → push *.
A işlenen → doğrudan çıktıya.
Girdi bitti: kalan operatör * çıktıya.
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
isEmpty(); yoksa istisna ya da null (kütüphaneye göre). Parantez kontrolünde "())" tam bu hatayı tetikler.top son öğenin indisidir, size = top + 1. Dolu koşulu top == kapasite − 1.veri[top--] = null yapılmazsa nesne çöp toplayıcıdan kaçar (bellek sızıntısı, loitering).5 2 − = 5 − 2 = 3, 2 − 5 değil.ArrayDeque for-each ile üstten alta, eski Stack ise alttan üste gezinir; çıktılar ters görünür.StackOverflowError oluşur (çağrı yığını taşar).Özet
Özet: Yığınla Çözülen Problemler
| Problem | Yığında tutulan | Zaman |
|---|---|---|
| push / pop / top / isEmpty / size | — | O(1) |
| Dize ters çevirme | karakterler | O(n) |
| Parantez kontrolü | eşleşmemiş açık parantezler | O(n) |
| Sonraki büyük öğe | sağdaki aday değerler (azalan) | O(n) |
| Stock span | henüz aşılmamış günlerin indisleri | O(n) |
| Hanoi kuleleri | alt görevler (n, kaynak, yard., hedef) | O(2n) |
| Infix → postfix / prefix | operatörler ve ( | O(n) |
| Postfix değerlendirme | ara sonuçlar | O(n) |
Min yığın (getMin) | her an en küçük değer | O(1) |
| Geri / ileri gezinme | ziyaret 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
Özet
Örnek Problemler
- 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?
{[()()]}ve{[(])}dizilerinden hangisi geçerlidir? Geçersiz olanda hata hangi karakterde fark edilir?(A + B) * (C − D) / Eifadesini postfix ve prefix biçimine çevirin.5 1 2 + 4 * + 3 −postfix ifadesinin değeri nedir?- Fiyatlar [10, 4, 5, 90, 120, 80] için span dizisini bulun.
- {2, 5, 3, 1, 4} dizisi için sonraki büyük öğe dizisini bulun.
Cevapları göster
- pop'lar: 3, 1, 8. Son yığın (alttan üste): 5, 4.
{[()()]}geçerli.{[(])}:]geldiğinde üstte(var → geçersiz.- Postfix:
A B + C D − * E /· Prefix:/ * + A B − C D E - 1+2 = 3, 3·4 = 12, 5+12 = 17, 17−3 = 14.
- [1, 1, 2, 4, 5, 1]
- {5, −1, 4, 4, −1}.
Özet
Alıştırmalar
- 1, 2, 3, 4 sırayla push ediliyor, araya pop'lar serpiştirilebiliyor.
2 4 3 1ve4 1 2 3çıkış sıraları mümkün mü? - Tek dizide iki yığın (biri baştan, biri sondan büyür): taşma koşulu nedir?
- Tek bir yardımcı yığınla bir yığını sıralayın. Karmaşıklık?
- Bir kelimenin palindrom olduğunu yığınla kontrol edin.
<b><i>…</i></b>gibi HTML etiketlerinin iç içeliği nasıl denetlenir?- Dizi dolunca 2 katı yerine +10 büyütülseydi n push'un toplam maliyeti?
İpuçları / cevaplar
2 4 3 1evet;4 1 2 3hayır (4 çıkınca üstte 3 var).- top1 = −1, top2 = N'den başlar; dolu:
top1 + 1 == top2. - x'i al; yardımcıda x'ten küçükleri geri taşı, x'i push et → O(n²).
- Harfleri push et, pop ile ters dizeyi kurup karşılaştır → O(n).
- Parantez algoritması: açılış etiketi push, kapanışta pop + ad eşleşmesi.
- 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
DequeveArrayDequeAPI belgeleri.