Treci la conținutul principal

Big O pentru memorie

Big O poate descrie memoria la fel cum descrie timpul: de cât spațiu suplimentar are nevoie algoritmul pe măsură ce intrarea crește?

Numărăm doar spațiul suplimentar — variabilele și slice-urile pe care le creează algoritmul — nu și datele de intrare. Acest spațiu suplimentar se numește spațiu auxiliar.

Spațiu O(1): un număr fix de variabile, indiferent de intrare. Spațiu O(n): se stochează ceva nou pentru fiecare element din intrare, de exemplu o copie sau un map cu valorile deja văzute.


Spațiu O(1)

Un algoritm care păstrează doar câteva variabile simple folosește spațiu constant. Slice-ul poate avea zece elemente sau zece milioane — algoritmul în sine tot doar contoarele lui le deține.

Adunarea, găsirea maximului și căutarea liniară au toate timp O(n), dar spațiu O(1): citesc mult și rețin aproape nimic.

Sumă cu o singură variabilă

Întregul algoritm reține exact un singur număr, sum. Memoria folosită nu crește odată cu slice-ul: spațiu O(1).

Prompt
Scrie un program Go care adună elementele slice-ului 3, 1, 4, 1, 5, 9, 2, 6 folosind o singură variabilă suplimentară și afișează suma, pentru a arăta un algoritm cu timp O(n), dar spațiu O(1).
Ieșire: program Go și rezultatul capturat
Sum with one variable

Spațiu O(n)

De îndată ce algoritmul construiește ceva cu câte o intrare pentru fiecare element — un slice nou, un map — spațiul lui este O(n): dublezi intrarea, dublezi memoria.

Asta nu este rău în sine. Este pur și simplu un cost pe care trebuie să îl observi, pentru că poate fi dat la schimb.

Construirea unui slice nou

Slice-ul de ieșire primește câte un element pentru fiecare element de intrare. 5 intră, 5 ies — memoria crește în pas cu intrarea: spațiu O(n).

Prompt
Scrie un program Go care construiește un slice nou cu fiecare element al slice-ului 1, 2, 3, 4, 5 dublat, folosind make și append, apoi afișează slice-ul de intrare și slice-ul de ieșire.
Ieșire: program Go și rezultatul capturat
Build a new slice

Spațiu dat la schimb pentru timp

De multe ori poți cheltui memorie ca să economisești timp. Exemplul clasic este Two Sum: găsește într-un slice două numere care adunate dau o valoare țintă.

Metoda directă verifică fiecare pereche — timp O(n²), spațiu O(1). Metoda rapidă parcurge slice-ul o singură dată și reține într-un map fiecare număr văzut. Pentru fiecare număr întreabă: „am văzut deja target - n?” — timp O(n), spațiu O(n).

De obicei schimbul merită: memorie se mai poate adăuga într-o mașină, timp nu.

Two Sum cu două bucle

Fiecare pereche este verificată până apare o potrivire — până la 15 verificări pentru 6 elemente, iar numărul lor crește cu pătratul intrării. Nu se folosește memorie suplimentară.

Prompt
Scrie un program Go care găsește două numere care adunate dau 9 în slice-ul 2, 11, 15, 1, 8, 7 verificând fiecare pereche cu două bucle imbricate și numărând verificările. Afișează fiecare potrivire împreună cu numărul de verificări de până atunci, iar la final afișează numărul total de verificări.
Ieșire: program Go și rezultatul capturat
Two Sum with two loops

Two Sum cu un map

O singură parcurgere, 6 verificări. Fiecare număr este căutat și stocat în map-ul seen — lucru în timp constant pentru fiecare element. Map-ul este memoria O(n) care cumpără timpul O(n).

Prompt
Scrie un program Go care găsește două numere care adunate dau 9 în slice-ul 2, 11, 15, 1, 8, 7 dintr-o singură parcurgere: ține un map cu numerele deja văzute și, pentru fiecare număr, verifică dacă ținta minus numărul a fost deja văzută. Afișează fiecare potrivire, iar la final afișează că fiecare element a fost verificat o singură dată.
Ieșire: program Go și rezultatul capturat
Two Sum with a map