Árvores
Igor Machado Coelho
05/10/2020 - 26/04/2023
São requisitos para essa aula:
Agradecimentos especiais ao prof. Fabiano Oliveira e prof. Fábio Protti, cujos conteúdos didáticos formam a base desses slides
A Árvore (do inglês Tree) é um Tipo Abstrato de Dado (TAD) que pode assumir duas formas:
Um conjunto de árvores é chamado floresta
Se T é uma árvore com raiz R, então:
Um caminho em uma árvore é uma sequência de nós com relação filho de ou pai de:
Exemplos:
O tamanho de um caminho consiste no número de nós. O nível de um nó é o tamanho de seu caminho até a raiz:
Nível de: A=1; C=2; F=3; H=4.
Desafio: em cursos de Teoria dos Grafos é provado que existe um único caminho conectando dois nós na árvore. Utilize sua intuição para verificar esta afirmação!
A altura de nó X é o tamanho do maior caminho que conecta X a uma folha descendente. Denotamos a altura de X por h(X):
Alturas: h(B)=1; h(C)=2; h(D)=3; h(A)=4.
A altura da árvore é a altura de sua raiz!
No exemplo, h(T)=h(A)=4.
Uma árvore é dita ordenada se há uma ordem associada aos filhos de cada nó.
Uma árvore é dita m-ária se cada nó é limitado a um máximo de m filhos.
A árvore acima é ternária (podendo também ser 4-ária, 5-ária, 6-ária, …), mas não é binária!
Em árvores binárias ordenadas de raiz R, a primeira subárvore de cada nó é denominada subárvore à esquerda de R (cuja raiz se chama filho esquerdo), e a segunda é a subárvore à direita de R (cuja raiz se chama filho direito).
Exemplo: B é filho esquerdo e C é filho direito de A
Uma árvore estritamente m-ária é aquela na qual cada nó possui exatamente 0 ou m filhos.
Exemplo: Considere a inclusão de um filho à esquerda de C.
Observação: Chamada pelo NIST de full binary tree, embora também seja preferivelmente chamada de própria (ou proper).
Uma árvore m-ária cheia (ou perfeita) é aquela na qual todo nó com alguma subárvore vazia está no último nível.
Observação: Chamada pelo NIST de perfect binary tree (ou perfect k-ary tree), embora também seja chamada de full ou, preferencialmente perfect.
Uma árvore m-ária completa é aquela na qual todo nó com alguma subárvore vazia está no último ou penúltimo níveis, estando os nós do último nível completamente preenchidos da esquerda para a direita.
Observação: Chamada pelo NIST de complete binary tree. Note que alguns autores consideram essa mesma definição para árvores cheias ou perfeitas. O ponto fundamental é a facilidade de implementação em vetores (vide próximos slides). [Knuth97]1
Qual a altura máxima de uma árvore binária com n nós?
Qual a altura máxima de uma árvore estritamente binária com n nós?
Qual a altura mínima de uma árvore binária com n nós?
Numa árvore binária cheia com n nós, qual o número de nós no último nível?
Solução: n, (n+1)/2, ⌈lg(n+1)⌉, (n+1)/2
Apresentaremos dois tipos de implementação para o TAD Árvore: Sequencial e Encadeada.
Note que, nesse momento, não apresentaremos operações sobre o TAD Árvore, focando somente em sua representação interna. A razão é que existem diversos tipos específicos de árvores, que apresentam operações distintas no TAD, de acordo com seu propósito.
Consideramos uma implementação de árvore m-ária, com alocação encadeada de nós (alocação interna sequencial para filhos).
Consideramos uma implementação de árvore m-ária, com alocação encadeada de nós.
Consideramos uma implementação de árvore m-ária, com alocação encadeada de nós.
Note que podemos reescrever os ponteiros de NoEnc2
com
os termos esq
e dir
(nó esquerdo e nó
direito).
Consideramos uma implementação de árvore binária, com alocação encadeada de nós.
Note que podemos reescrever os ponteiros de NoEnc3
utilizando unique_ptr
, para maior segurança:
Observamos pelas implementações NoEnc2
e
NoEnc3
que uma árvore
m-ária
qualquer pode ser convertida para uma árvore binária. Isso
reforça a importância do estudo de implementações eficientes para
árvores binárias.
As Árvores com Implementação Sequencial utilizam um array para armazenar os dados. Assim, os dados sempre estarão em um espaço contíguo de memória.
Desafio: quanto espaço é necessário para armazenar uma árvore qualquer com altura h?
Consideraremos uma árvore sequencial com, no máximo,
MAX_N
elementos do tipo caractere.
constexpr int MAX_N = 50; // capacidade máxima da árvore
class ArvoreSeq1
{
public:
char elem [MAX_N]; // elementos na fila
};
Desafio: Quantos níveis cabem nessa árvore? ⌈log2(50+1)⌉=6
Note que, para esse fim, somente as árvores completas terão maior eficiência, utilizando uma representação por níveis.
Desafio: onde fica o primeiro elemento de cada nível da árvore T?
Dado um nó V na posição i da árvore sequencial T, em que posição estão:
Resposta: considerando contagem 1..MAX_N, estarão respectivamente nas posições ⌊i/2⌋ (pai), 2i e 2i+1 (filhos).
Desafio: considere a contagem 0..MAX_N-1 e refaça o cálculo.
Solução: posições ⌊(i−1)/2⌋ (pai), 2i+1 e 2i+2 (filhos).
Fim parte de implementações.
Como “imprimir” uma árvore?
Estruturas lineares tem uma intuição mais direta para o conceito de impressão, mas para estruturas arbóreas isso já não é tão direto. Além da impressão, muitas vezes é desejável efetuar outras operações ou visitas em nós de uma árvore.
Operações de Percursos em Árvore (do inglês, tree traversals) apresentam uma solução para isso:
No percurso de pré-ordem, o nó é visitado primeiro, depois os filhos esquerdos, e finalmente, são visitados os filhos direitos.
No percurso de pós-ordem, os filhos esquerdos são visitados primeiro, depois os filhos direitos, e finalmente o nó é visitado.
No percurso em-ordem, os filhos esquerdos são visitados primeiro, depois o nó é visitado, e finalmente os filhos direitos são visitados.
Apresente o percurso de pré-ordem para as árvores abaixo:
Solução: 1. ABCDFGE 2. ABCD 3. ABCD 4. ABDECFG
Apresente o percurso de pós-ordem para as árvores abaixo:
Solução: 1. BFGDECA 2. DCBA 3. DCBA 4.DEBFGCA
Apresente o percurso de ordem simétrica para as árvores abaixo:
Solução: 1. BAFDGCE 2. DCBA 3. ABCD 4. DBEAFCG
Fim parte de percursos.
Além da bibliografia do curso, recomendamos para esse tópico:
Em especial, agradeço aos colegas que elaboraram bons materiais, como o prof. Fabiano Oliveira (IME-UERJ), e o prof. Jayme Szwarcfiter cujos conceitos formam o cerne desses slides.
Estendo os agradecimentos aos demais colegas que colaboraram com a elaboração do material do curso de Pesquisa Operacional, que abriu caminho para verificação prática dessa tecnologia de slides.
Esse material de curso só é possível graças aos inúmeros projetos de código-aberto que são necessários a ele, incluindo:
Agradecimento especial a empresas que suportam projetos livres envolvidos nesse curso:
Esses slides foram escritos utilizando pandoc, segundo o tutorial ilectures:
Exceto expressamente mencionado (com as devidas ressalvas ao material cedido por colegas), a licença será Creative Commons.
Licença: CC-BY 4.0 2020
Igor Machado Coelho
[Knuth97] Donald E. Knuth, The Art of Computer Programming, Addison-Wesley, volumes 1 and 2, 2nd edition, 1997.↩︎