map — dicționar ordonat cheie-valoare

Mediu~17 min15 pași

De ce contează?

Gândește-te la agenda de pe telefon. Cauți după nume și primești numărul. Fiecare nume apare o singură dată, iar lista e afișată ordonată alfabetic, ca să găsești repede. Asta este un map: cauți după o cheie unică și primești valoarea ei, totul ținut ordonat după cheie.

Ce este un map și de ce îl folosești

Un map este un dicționar cheie → valoare. Fiecare cheie e unică (nu poți avea două intrări cu aceeași cheie) și cheile sunt ținute ordonate crescător, pentru că map e construit pe un arbore binar echilibrat.

De ce contează asta în concursuri? Pentru că un vector de frecvență obișnuit (frec[x]++) funcționează doar când x e un întreg mic, pe care îl poți folosi ca indice. Dar dacă vrei să numeri cuvinte, numere uriașe (până la 10^18) sau perechi, nu ai cum să declari un vector atât de mare. map rezolvă exact asta: cheia poate fi orice tip comparabil, iar fiecare operație costă O(log n).

Pe scurt, alegi map când:

  • ai chei care nu sunt indici mici (string-uri, numere mari, perechi);
  • vrei rezultatul parcurs sortat după cheie, fără efort suplimentar.

Exemplu pas cu pas: frecvența cuvintelor

Vrem să numărăm de câte ori apare fiecare cuvânt în „mar pere mar mar pere”. Folosim un map<string, int> și incrementăm cu m[cuvant]++:

  • citim „mar” → m["mar"] nu există, devine 0, apoi ++mar: 1
  • citim „pere” → devine 0, apoi ++mar: 1, pere: 1
  • citim „mar” → există, ++mar: 2, pere: 1
  • citim „mar” → ++mar: 3, pere: 1
  • citim „pere” → ++mar: 3, pere: 2
frec
3
2
cheie
mar
pere
Cheile sunt ordonate alfabetic. Parcurgerea dă întâi „mar”, apoi „pere”.

Truc-ul m[cuvant]++ merge tocmai datorită efectului secundar al lui []: la prima întâlnire cheia e creată cu 0, apoi incrementată la 1. La numărare e exact ce vrei — dar reține că acest efect te poate încurca dacă doar citești.

Implementare C++

#include <iostream>
#include <map>
#include <string>
using namespace std;

int main() {
    map<string, int> frec;

    // numaram cuvintele citite
    string cuvant;
    while (cin >> cuvant) {
        frec[cuvant]++;            // creeaza cheia la 0 daca lipseste, apoi ++
    }

    // verificare PURA: nu cream cheia daca nu exista
    if (frec.count("mar")) {
        cout << "mar apare de " << frec["mar"] << " ori\n";
    }

    // find ne da un iterator catre pereche (sau end() daca lipseste)
    auto it = frec.find("kiwi");
    if (it == frec.end()) {
        cout << "kiwi nu exista\n";
    }

    // parcurgere SORTATA dupa cheie, cu iterator clasic
    for (auto it = frec.begin(); it != frec.end(); it++) {
        cout << it->first << " -> " << it->second << "\n";
        // it->first  = cheia, it->second = valoarea
    }

    // varianta moderna, mai citibila (structured bindings)
    for (auto& [k, v] : frec) {
        cout << k << " : " << v << "\n";
    }
    return 0;
}

Pentru intrarea „mar pere mar mar pere”, parcurgerea afișează cheile ordonate: mar -> 3 apoi pere -> 2.

Complexitate

Toate operațiile trec prin arbore, deci sunt logaritmice — nu O(1) ca la vector:

OperațieTimpObservație
m[cheie] (acces/creare)O(log n)poate insera cheia
insertO(log n)nu suprascrie o cheie existentă
find / countO(log n)citire pură
parcurgere completăO(n)în ordinea sortată a cheilor
Observația-cheie

Parcurgerea unui map îți dă cheile crescător, gratis. Dacă ai nevoie de rezultatul sortat (ex. afișează frecvențele în ordine alfabetică), map îți economisește un sort separat — exact diferența față de un dicționar neordonat.

Greșeli frecvente

Greșeli frecvente cu map:

  • m[cheie] la simpla citire creează intrarea: if (m[x] > 0) adaugă pe x cu valoarea 0 dacă lipsea. Pentru verificare pură folosește m.count(x) sau m.find(x) != m.end().
  • Crezi că insert suprascrie: m.insert({k, v}) NU schimbă valoarea dacă cheia există deja. Pentru suprascriere folosește m[k] = v.
  • Ordinea de inserare ≠ ordinea de parcurgere: map parcurge sortat după cheie, nu în ordinea în care ai adăugat. Dacă vrei ordinea de inserare, map nu e structura potrivită.
  • Folosești map unde unordered_map e mai rapid: dacă NU ai nevoie de chei ordonate, unordered_map dă O(1) mediu în loc de O(log n). map merită doar când ordinea contează.

Vizualizare

Urmărește cum o cheie nouă se așază la locul ei în arbore, păstrând totul ordonat:

Indiciu

Observă unde aterizează o cheie nouă: nu la sfârșit, ci în poziția care menține ordinea. De aceea parcurgerea iese mereu sortată. Folosește / pentru a vedea fiecare inserare pas cu pas.

Recapitulare

  • map e dicționar cheie unică → valoare, ținut ordonat după cheie, cu operații O(log n).
  • m[cheie] creează cheia cu valoare implicită la citire — pentru verificare pură folosește count sau find.
  • Parcurgi sortat cu it->first / it->second sau cu for (auto& [k, v] : m); alege unordered_map când ordinea nu contează.

Întrebarea 1 / 3

Ce face `m[cheie]` dacă tocmai citești valoarea, iar cheia NU există încă în map?