Árvores
• Estrutura composta por nós e arcos direcionados – Um dos nós é a raiz da árvore.
– Todo nó (exceto a raiz), possui um nó pai. – Existe um único caminho da raíz até um nó.
– Uma árvore cujos nós possuem no máximo 2 nós filhos é uma árvore binária.
A
B
C
D
E
F
G
H
I
J
K
0
1
2
3
Profundidade
da Árvore
Altura da árvore
= maior
profundidade + 1
Árvores – Implementação Genérica
• Cada nó precisa possuir uma referência para cada um dos seus nós filhos.
• Os filhos de um nó podem ser armazenados em uma lista ligada dentro do nó.
• Uma implementação deste tipo é recursiva (cada nó é um nó raiz de uma sub-árvore).
• Uma consequência da recursividade desta estrutura de dados é que as funções que a manipulam também são recursivas.
Árvores – Aplicações
• Sistema de Arquivos e Diretórios (Unix, Linux, Windows)
• Suponha que queiramos imprimir o nome de arquivos a partir de um diretório:
Imprimir():
Imprimir o nome do arquivo/diretório corrente Para cada arquivo/diretório x
dentro do diretório corrente x.imprimir()
Árvores – Função Recursiva
• Note que esta é uma função recursiva. Neste caso, a visita aos nós ocorre em uma ordem chamada anterior. Poderíamos ter delegado a impressão aos nós filhos, antes da impressão dos nós pai, neste
A
B
C
D
E
F
G
H
I
J
K
A B F
G
C
D H
E I
J K
F G B
C
H D
I K J E
A
Árvores – Função Recursiva
Exercícios:• Implementar uma função recursiva para calcular a altura de uma árvore.
• Implementar uma função recursiva para calcular o número de nós de uma árvore.
Árvores Binárias - Implementação
• Árvores cujos nós possuem no máximo 2 nós filhos.
public class NoBinario { Object dado;
NoBinario esquerdo, direito; NoBinario(Object x) {
dado = x;
esquerdo = null; direito = null; }
NoBinario (Object x, NoBinario e, NoBinario d) { dado = x;
esquerdo = e; direito = d; }
Árvores Binárias - Implementação
public int tamanho(){int tamanhoEsquerdo = 0; int tamanhoDireito = 0; if (esquerdo !=null) tamanhoEsquerdo = esquerdo.tamanho(); if (direito !=null) tamanhoDireito = direito.tamanho();
return (1 + tamanhoEsquerdo + tamanhoDireito); }
}
Árvores Binárias - Exemplo
Exemplo: Árvore de expressões
+
a
*
-
d
b
c
Folhas são variáveis
demais nós são operações
A partir da raiz imprima: nó esquerdo
o próprio nó nó direito
(a + ((b-c)*d))
Ordem interior
Procedimentos de iteração
Muitas aplicações exigem que todos os nós de uma árvore sejam
visitados. Para simplificar, vamos considerar apenas árvores
binárias.
1
2
3
4
6
5
7
7
1
6
3
5
2
4
2
1
5
3
7
4
6
Ordem anterior
Prefixado
Ordem posterior
Posfixado
Ordem interior
Interfixado
Procedimentos de iteração - Implementação
execute:
execute Atividade;
if (left!=null)
left.execute();
if (rigth!=null)
rigth.execute();
1
2
3
4
6
5
7
Ordem anterior
Prefixado
Procedimentos de iteração - Implementação
7
1
6
3
5
2
4
Ordem posterior
Posfixado
execute:
if (left!=null)
left.execute();
if (rigth!=null)
rigth.execute();
execute Atividade;
Procedimentos de iteração - Implementação
2
1
5
3
7
4
6
Ordem interior
Interfixado
execute:
if (left!=null)
left.execute();
execute Atividade;
if (rigth!=null)
rigth.execute();
Importante
Implementações recursivas são simples (abordagem muito
voltada a a resolução de problemas) mas do ponto de vista
de utilização de recursos computacionais são soluções
pouco eficientes pois todas operações fazem uso intensivo
do Stack (pilha) do interpretador.
Procedimento de iteração adicional
Iteração por nível: quando um nó é visitado, seus filhos são
adicionados à uma fila.
NO A: B C D E
Busca em Largura
1
2
3
4
5
6
7
8
9
10
11
NO B: C D E F G
NO C: D E F G
NO D: E F G H
NO E: F G H I J
NO F: G H I J
NO G: H I J
NO H: I J
NO I: J
NO J: K
NO K:
A
B
C
D
E
F
G
H
I
J
K
Árvores Binárias de Busca
Para realizar uma busca de forma genérica, consideramos que
cada objeto possui um identificador (ou chave, “key”).
Em uma árvore de busca binária:
• cada nó na sub-árvore esquerda de um nó x tem um
identificador menor que x
• cada nó na sub-árvore direita de x tem identificador maior
que x.
Árvores Binárias de Busca
Em uma árvore de busca binária uma ordem interior passa
pelos identificadores em ordem crescente.
7
2
9
1
5
3
Ordem interior:
1 2 3 5 7 9
Árvores Binárias de Busca - Operações
Find ()
inicie na raiz e mova para direita ou esquerda até
encontrar o elemento.
Minimo()
sempre a esquerda
Maximo()
sempre a direita
Remove()
encontra o nó que será removido:
se o nó não tem filhos, remova
se o nó tem um filho, substitua o nó pelo filho
se o nó tem dois filhos, substitua:
pelo menor item na árvore direita e o remova
pelo maior item na árvore esquerda e o remova
Árvores Binárias de Busca - Operações
Remoção apresenta dificuldade no caso em que o nó
removido possui dois filhos:
X
R
A
B
Note que X é maior que A e B:
AeB não podem ser filhos de X
Árvores Binárias de Busca - Operações
Se substituirmos o nó removido pelo menor elemento da
subárvore direita resolvemos o problema:
7
2
1
5
Remove(2)
9
3
4
7
3
1
5
9
3
4
7
3
1
5
9
4
Árvore Binária de Busca
public class NoBinBusca {int dado;
NoBinBusca esq, dir;
public NoBinBusca (int d) { dado = d;
esq = null; dir = null; }
public NoBinBusca minimo(){ NoBinBusca n = this;
while (n.esq !=null) n = n.esquerdo; return (n);
Árvore Binária de Busca
public NoBinBusca maximo(){NoBinBusca n = this;
while (n.dir !=null) n = n.dir; return (n);
}
public NoBinBusca busca(int x) { if (x<dado) {
if (esq!=null) return esq.busca(x); else return (null);
} else if (x>dado){
if (dir !=null) return dir.busca(x); else return (null);
} else return (this); }
Árvore Binária de Busca
public void insere(int x){if (x<dado) {
if (esq != null) esq.insere(x); else esq = new NoBinBusca(x); } else if (x>dado) {
if (dir!=null) dir.insere(x); else dir = new NoBinBusca(x); } else return;
Árvore Binária de Busca
//retorna o nó raiz da sub-árvore public NoBinBusca remove(int x){if (x<dado) {
if (esq != null) esq.remove(x); } else if (x>dado) {
if (dir!=null) dir.remove(x); } else return(retira());
return(this); }
Árvore Binária de Busca
private NoBinBusca retira() {if (esq == null) return (dir); else if (dir ==null) return (esq); else {
if (dir.esq ==null) { dado = dir.dado; dir = dir.dir;
} else {
NoBinBusca pai = dir; NoBinBusca filho = pai.esq; while (filho.esq != null) {
pai = filho; filho = pai.esq; } dado = filho.dado; pai.esq = filho.dir; }