Le risposte precedenti riguardano solo le alternative dell'albero e il rosso nero probabilmente rimane solo per ragioni storiche.
Perché non una tabella hash?
Un tipo richiede solo l' <
operatore (confronto) da utilizzare come chiave in un albero. Tuttavia, le tabelle hash richiedono che ogni tipo di chiave abbia unhash
funzione definita. Mantenere i requisiti di tipo al minimo è molto importante per la programmazione generica, quindi è possibile utilizzarlo con un'ampia varietà di tipi e algoritmi.
La progettazione di una buona tabella hash richiede una conoscenza intima del contesto in cui verrà utilizzata. Dovrebbe utilizzare l'indirizzamento aperto o il concatenamento collegato? Quali livelli di carico dovrebbe accettare prima del ridimensionamento? Dovrebbe usare un hash costoso che evita le collisioni o uno che è approssimativo e veloce?
Poiché l'STL non può prevedere quale sia la scelta migliore per la propria applicazione, l'impostazione predefinita deve essere più flessibile. Gli alberi "funzionano" e si adattano bene.
(C ++ 11 ha aggiunto tabelle hash unordered_map
. Dalla documentazione è possibile vedere che richiede l'impostazione delle politiche per configurare molte di queste opzioni.)
E gli altri alberi?
Gli alberi Red Black offrono una ricerca rapida e si bilanciano da soli, a differenza dei BST. Un altro utente ha sottolineato i suoi vantaggi rispetto all'albero AVL auto-bilanciante.
Alexander Stepanov (Il creatore di STL) ha detto che avrebbe usato un albero B * invece di un albero rosso-nero se avesse scritto di std::map
nuovo, perché è più amichevole per le moderne cache di memoria.
Uno dei maggiori cambiamenti da allora è stata la crescita delle cache. I cache miss sono molto costosi, quindi la località di riferimento è molto più importante ora. Le strutture dati basate su nodo, che hanno una bassa località di riferimento, hanno molto meno senso. Se progettassi STL oggi, avrei un diverso set di contenitori. Ad esempio, un albero B * in memoria è una scelta molto migliore di un albero rosso-nero per l'implementazione di un contenitore associativo. - Alexander Stepanov
Le mappe dovrebbero sempre usare alberi?
Un'altra possibile implementazione delle mappe sarebbe un vettore ordinato (ordinamento per inserzione) e una ricerca binaria. Funzionerebbe bene per i contenitori che non vengono modificati spesso ma che vengono interrogati frequentemente. Lo faccio spesso in C come qsort
e bsearch
sono integrati.
Devo anche usare la mappa?
Considerazioni sulla cache significano che raramente ha senso usare std::list
o std::deque
oltrestd:vector
anche per quelle situazioni che ci hanno insegnato a scuola (come rimuovere un elemento dal centro dell'elenco). Applicando lo stesso ragionamento, utilizzare un ciclo for per la ricerca lineare di un elenco è spesso più efficiente e più pulito rispetto alla creazione di una mappa per alcune ricerche.
Naturalmente la scelta di un contenitore leggibile è in genere più importante delle prestazioni.