Sparround

Stack (LIFO)

StackLIFO (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.

MeyarMassiv əsaslıLinked list əsaslı
pushamortized O(1) — sona əlavəO(1) — başa əlavə
pop / peekO(1) — sondanO(1) — başdan
YaddaşSıx; boş tutum bir qədər israf ola bilərNode başına əlavə pointer
BöyüməTutum dolanda O(n) köçürmə (nadir)Köçürmə yoxdur
Cache lokallığıYaxşı — praktikada daha sürətliPis
Ölçü limitiYaddaş qədərYaddaş qədər
Praktik seçimDefault — 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.