- Apter Trees em C++
- Uma árvore representada de forma simples usando apenas dois vetores: [valor do nó, índice do pai)
- A maioria das árvores é implementada como árvores binárias, com cada nó contendo seu valor e ponteiros para os nós filho da esquerda/direita
- Esse tipo de estrutura de dados costuma exigir implementação recursiva e pode ficar lenta por causa do comportamento de cache e de chamadas frequentes a
malloc()
- O conceito de quem “possui” um nó da árvore pode se tornar complexo em software com múltiplas camadas
- As Apter Trees são muito mais rápidas, mais fáceis de raciocinar e mais simples de implementar
Como funciona
- Implementada com dois arrays do mesmo tamanho
- vetor (array) de dados
d
- vetor
p de índices dos pais. O índice do vetor d é usado como chave (c)
- com frequência, a chave/índice
c é um inteiro
- Se Coco for mãe de Molly e Arca, e Arca tiver um filho chamado Cricket, a estrutura fica assim
tree.d = ["Coco", "Molly", "Arca","Cricket"]
tree.p = [0,0,0,2]
- O nó cujo pai é 0 e cuja chave é 0 é o nó raiz (em Apter Trees, o nó raiz é obrigatório, ou então
-1 significa “sem pai”, mas isso é ignorado aqui)
- O computador consegue manipular vetores muito rapidamente. Como isso é bem mais rápido do que operações com ponteiros, comparar a notação Big-O dos algoritmos na prática não faz muito sentido
Por que isso importa
- Esse método de implementação é a forma de representar árvores mais elegante que o autor já viu
- Com uma biblioteca adequada de operações sobre vetores, fica fácil de entender e de encontrar bugs
- Por ser simples, pode ser adaptado facilmente para outros cenários de uso
- É possível iterar rapidamente pelos valores ignorando o vetor de índices dos pais, o que equivale a um Deep Map disponível “de graça”
- É amigável para GPU e fácil de usar em ambientes embarcados
- Também é fácil manter a segurança impedindo que o tamanho dos vetores cresça além de um limite específico
Origem
- O autor está tentando descobrir quem inventou essa técnica e supõe que ela já tinha um nome no período orientado a vetores das décadas de 1960 e 1970
- A primeira descrição completa que viu foi a explicada por Apter, mas ela também está amplamente documentada em K3
- APL implementa árvores da forma tradicional, mas usa técnica semelhante para grafos vetoriais
- A técnica também é conhecida entre usuários de J, e há uma explicação sobre implementação de árvores em J no Rosetta Code
- John Earnest explica com mais detalhes a implementação de árvores vetoriais, incluindo a abordagem de “índice por deslocamento” para tratar exclusões
- Uma abordagem mais complexa é rastrear a profundidade de cada item
Outras implementações comuns de árvore
- Há implementações como a árvore do kernel do FreeBSD, as árvores do klib, a classe Tree do Ruby e classes de árvore declarativas em Python
- Elas não oferecem exatamente a mesma funcionalidade que Apter Trees, e algumas são muito maiores por causa da generalização
Estado atual deste código
- Foi uma tentativa de implementar isso enquanto aprendia C++17
- Ainda não está pronto para uso e o autor precisa aprender mais sobre C++
Opinião do GN⁺
- Apter Trees simplificam estruturas de árvore tradicionalmente complexas, permitindo um gerenciamento de dados rápido e eficiente
- Uma implementação de árvore baseada em vetores pode minimizar a sobrecarga de memória e melhorar o desempenho por meio de acesso linear
- Este artigo é interessante por explorar uma abordagem inovadora de estruturas de dados em engenharia de software
1 comentários
Comentários do Hacker News
mallocse melhorar a localidade de dados, valeria a pena tentar uma implementação tradicional de árvore usando um pool de nós.mallocpor causa da necessidade de adicionar ou remover nós dinamicamente. Observou ainda que, se o tamanho da árvore for limitado e exclusões forem raras, uma implementação baseada em array pode ser adequada.