Stack (LIFO)
Stack — LIFO (Last In, First Out) prinsipi ilə işləyən kolleksiyadır: sonuncu qoyulan element birinci çıxarılır. Zehni model — boşqab yığını: yalnız üstdən qoyub üstdən götürürsən.
Stack abstrakt data tipidir (ADT) — yəni davranışla təyin olunur, konkret implementasiya ilə yox. Üç əsas əməliyyat, hamısı O(1):
- push(x) — üstə element qoy
- pop() — üstdəki elementi çıxar və qaytar
- peek() (və ya
top) — üstdəkinə bax, çıxarmadan
Əlavə: isEmpty(), size.
Stack-in gücü məhz məhdudluğundadır: ortadan element götürmək, indekslə çıxış, axtarış — heç biri interfeysə daxil deyil. Bu məhdudiyyət niyyəti kodda açıq ifadə edir və səhv istifadəni qeyri-mümkün edir.
| Meyar | Massiv əsaslı | Linked list əsaslı |
|---|---|---|
| push | amortized O(1) — sona əlavə | O(1) — başa əlavə |
| pop / peek | O(1) — sondan | O(1) — başdan |
| Yaddaş | Sıx; boş tutum bir qədər israf ola bilər | Node başına əlavə pointer |
| Böyümə | Tutum dolanda O(n) köçürmə (nadir) | Köçürmə yoxdur |
| Cache lokallığı | Yaxşı — praktikada daha sürətli | Pis |
| Ölçü limiti | Yaddaş qədər | Yaddaş qədər |
| Praktik seçim | Default — JS `Array`, Kotlin `ArrayDeque`, Dart `List` | Yalnız xüsusi tələb olanda |
Call stack — stack-in ən vacib real nümunəsi. Proqramın funksiya çağırışlarını idarə etməsi tam olaraq LIFO-dur: funksiya çağırılanda frame push olunur, return olanda pop. Ən son çağırılan funksiya birinci qayıdır.
Bu əlaqəni anlamaq iki şeyi izah edir:
- Stack trace niyə tərsinə oxunur — ən üstdəki sətir ən son çağırışdır
- Stack overflow niyə baş verir — pop olunmayan push-lar məhdud stack-i doldurur
Bundan çıxan praktik nəticə: hər rekursiv alqoritm açıq stack ilə iterativ yazıla bilər. Ağac DFS-də call stack-in yerinə ArrayDeque qoyursan — məntiq eyni qalır, amma yaddaş heap-də olduğu üçün dərinlik limiti aradan qalxır.
Klassik istifadə ssenariləri — stack "son açılanı birinci bağla" məntiqinin olduğu hər yerdə görünür.
- Mötərizələrin uyğunluğu — açılan mötərizəni push et, bağlanan gələndə pop edib uyğunluğu yoxla. Sonda stack boş olmalıdır. Bu, kompilyator və linter-lərin əsas texnikasıdır;
O(n)time,O(n)space. - Undo — hər əməliyyat push olunur, geri qaytarma pop edir (redo üçün ikinci stack).
- İfadələrin hesablanması — infix ifadəni postfix-ə (RPN) çevirmək (shunting-yard) və postfix-i hesablamaq: operand push, operator gələndə iki operand pop, nəticəni push.
- DFS (dərinlik üzrə axtarış) — qraf/ağac gəzintisi; BFS-in növbə (queue) istifadə etməsinin əksinə, DFS stack işlədir.
- Geri izləmə (backtracking) — labirint, sudoku, N-Queens: vəziyyəti push et, dalan olanda pop edib başqa yol seç.
- Brauzer geri düyməsi, mətn redaktorunda dırnaq/teq balansı, monotonic stack məsələləri (növbəti böyük element, histoqramda ən böyük düzbucaqlı).
İnterview məsləhəti. "Mötərizələrin balansını yoxla" ən çox verilən stack sualıdır. Güclü cavab bu addımları göstərir: açılan mötərizələri push et; bağlanan gələndə əvvəlcə stack-in boş olub-olmadığını yoxla (boşdursa dərhal false), sonra pop edib növ uyğunluğunu yoxla; dövr bitəndə stack boş olmalıdır ("(((" üçün false). İki sərhəd halını demək — boş giriş true, artıq bağlanan mötərizə false — müsahibin gözlədiyi diqqətdir.
Müsahiblərin sevdiyi əlavə sual: "`getMin()` əməliyyatı `O(1)` olan stack necə qurarsan?" Cavab: ikinci stack saxla — hər push-da cari minimumu ora da push et, pop-da hər ikisindən pop et. Yaddaş O(n), bütün əməliyyatlar O(1). Bu sual sənin "əlavə yaddaşla vaxt qazanmaq" düşüncəni yoxlayır.
Ən çox rast gəlinən səhvlər: boş stack-dən pop etməzdən əvvəl yoxlama qoymamaq; dövrün sonunda stack-in boş olmasını yoxlamamaq; JS-də Array-i stack kimi işlədəndə shift()/unshift() (bunlar O(n)-dir) istifadə etmək — düzgün cütlük push()/pop()-dur.
📚 Mənbələr və sənədlər
- java.util.ArrayDequerəsmidocs.oracle.com
Sənəd açıq deyir ki, stack üçün Stack-dən, queue üçün LinkedList-dən sürətlidir.