Parece que em todo lugar que olho, as estruturas de dados estão sendo implementadas usando árvores vermelho-pretas ( std::set
em C ++, SortedDictionary
em C # etc.)
Tendo acabado de cobrir (a, b), vermelho-preto e árvores AVL na minha classe de algoritmos, aqui está o que eu descobri (também perguntando por professores, olhando alguns livros e pesquisando um pouco):
- As árvores AVL têm profundidade média menor que as árvores vermelho-preto e, portanto, a busca de um valor na árvore AVL é consistentemente mais rápida.
- As árvores preto-vermelho fazem menos mudanças estruturais para se equilibrar do que as árvores AVL, o que poderia torná-las potencialmente mais rápidas para inserção / exclusão. Estou dizendo potencialmente, porque isso dependeria do custo da alteração estrutural da árvore, pois dependerá muito do tempo de execução e implementação (também pode ser completamente diferente em uma linguagem funcional quando a árvore é imutável?)
Existem muitos benchmarks online que comparam as árvores AVL e Red-black, mas o que mais me impressionou é que meu professor disse basicamente que, geralmente, você faria uma de duas coisas:
- Ou você realmente não se importa muito com o desempenho; nesse caso, a diferença de 10 a 20% entre AVL e vermelho-preto na maioria dos casos não importa.
- Ou você realmente se preocupa com o desempenho, no caso de abandonar as árvores AVL e Vermelho-preto e optar por árvores B, que podem ser aprimoradas para funcionar muito melhor (ou (a, b) -árvores, eu ' vou colocar todos em uma cesta.)
A razão disso é que uma árvore B armazena dados de forma mais compacta na memória (um nó contém muitos valores) e haverá muito menos erros de cache. Você também pode ajustar a implementação com base no caso de uso e fazer com que a ordem da árvore B dependa do tamanho do cache da CPU, etc.
O problema é que não consigo encontrar quase nenhuma fonte que analise o uso na vida real de diferentes implementações de árvores de pesquisa em hardware moderno real. Examinei muitos livros sobre algoritmos e não encontrei nada que comparasse diferentes variantes de árvores, além de mostrar que uma tem profundidade média menor que a outra (o que realmente não diz muito sobre como a árvore se comportará). em programas reais.)
Dito isto, existe uma razão específica para o uso de árvores vermelho-pretas em todos os lugares, quando, com base no que foi dito acima, as árvores B devem superá-las? (como a única referência que eu pude encontrar também mostra http://lh3lh3.users.sourceforge.net/udb.shtml , mas pode ser apenas uma questão de implementação específica). Ou é a razão pela qual todo mundo usa árvores vermelho-pretas porque elas são fáceis de implementar ou, em outras palavras, difíceis de implementar mal?
Além disso, como isso muda quando se muda para o domínio das linguagens funcionais? Parece que Clojure e Scala usam tentativas mapeadas de matriz Hash , onde Clojure usa um fator de ramificação de 32.