As respostas anteriores tratam apenas de alternativas em árvore e o preto vermelho provavelmente permanece apenas por razões históricas.
Por que não uma tabela de hash?
Um tipo requer apenas que o <
operador (comparação) seja usado como uma chave em uma árvore. No entanto, as tabelas de hash exigem que cada tipo de chave tenha uma hash
função definida. Manter os requisitos de tipo no mínimo é muito importante para a programação genérica, para que você possa usá-lo com uma grande variedade de tipos e algoritmos.
Projetar uma boa tabela de hash requer um conhecimento íntimo do contexto em que ela será usada. Ele deve usar endereçamento aberto ou encadeamento vinculado? Quais níveis de carga ele deve aceitar antes de redimensionar? Deve usar um hash caro que evita colisões, ou um que seja áspero e rápido?
Como o STL não pode prever qual é a melhor opção para o seu aplicativo, o padrão precisa ser mais flexível. Árvores "simplesmente funcionam" e escalam bem.
(O C ++ 11 adicionou tabelas de hash com unordered_map
. Você pode ver na documentação necessária para definir políticas para configurar muitas dessas opções.)
E as outras árvores?
As árvores Red Black oferecem pesquisa rápida e são auto balanceadas, diferentemente das BSTs. Outro usuário apontou suas vantagens sobre a árvore AVL auto-balanceada.
Alexander Stepanov (o criador do STL) disse que usaria uma árvore B * em vez de uma árvore Vermelho-Preto se ele escrevesse std::map
novamente, porque é mais amigável para os caches de memória modernos.
Uma das maiores mudanças desde então tem sido o crescimento de caches. As falhas de cache são muito caras, portanto a localidade de referência é muito mais importante agora. Estruturas de dados baseadas em nós, com baixa localidade de referência, fazem muito menos sentido. Se eu estivesse projetando o STL hoje, teria um conjunto diferente de contêineres. Por exemplo, uma árvore B * na memória é uma escolha muito melhor do que uma árvore vermelho-preta para implementar um contêiner associativo. - Alexander Stepanov
Os mapas devem sempre usar árvores?
Outra possível implementação de mapas seria um vetor classificado (classificação por inserção) e pesquisa binária. Isso funcionaria bem para contêineres que não são modificados com frequência, mas são consultados com frequência. Costumo fazer isso em C como qsort
e bsearch
são incorporados.
Eu preciso mesmo usar o mapa?
Considerações de cache significa que raramente faz sentido usar std::list
ou std::deque
maisstd:vector
mesmo para aquelas situações que aprendemos na escola (como remover um elemento do meio da lista). Aplicando esse mesmo raciocínio, o uso de um loop for na pesquisa linear de uma lista geralmente é mais eficiente e mais limpo do que construir um mapa para algumas pesquisas.
É claro que escolher um contêiner legível geralmente é mais importante que o desempenho.