View
225
Download
0
Category
Preview:
Citation preview
Árvores
Organização dos dados: ◦ Linear:
Listas, pilhas, filas.
Relação sequencial.
◦ Não-linear:
Outros tipos de relação entre dados;
Hierarquia;
Árvores
Grafos.
3
Árvores
Estrutura não-linear que representa uma relação de hierarquia
Exemplo:
◦Árvore de diretórios;
◦Árvore genealógica.
4
Árvores 6
Estrutura de Dados
Capítulo 1
1.1 Listas
1.2 Pilha
1.3 Fila
Capítulo 2
2.1 Recursividade
2.1.1 Hanoi
Capítulo 3
3.1 Árvores
3.2 Árvores binárias
(a + (b * ( (c / d) - e) )
Árvores – Representação
Diagrama de Inclusão
Parentes aninhados
7
( A (B) ( C (D (G) (H)) (E) (F (I)) ) )
Árvores
Caracterização de uma árvore: ◦ Composta por um conjunto de nós
◦ Existe um nó r, chamado nó raiz
Este nó contém zero ou mais sub-árvores, cujas raízes são ligadas diretamente a r
Os nós raízes das sub-árvores são ditos filhos do nó pai r ◦ “Nós” que não têm filhos são chamados de folhas
É tradicional desenhar as árvores com a raiz para cima e folhas para baixo
8
Árvores
◦Um nó filho não pode ter dois pais.
◦O filho não sabe quem é o pai.
9
Este exemplo não é árvore! É um grafo.
Árvores
O número de sub-árvores de um nó determina o “grau de saída“ desse nó. Ou seja, a quantidade de filhos.
Grau Máximo, ou grau da árvore ◦ Maior grau de seus nós.
Nós que não tem grau (grau == 0), são denominados de nó folha
Para identificar os nós de uma estrutura, usamos a relação de hierarquia existente em uma árvore genealógica ◦ Filho, pai, neto, irmão
11
Árvores
Nível ◦ Representa a distância de um nó até a raiz
◦ O nó de maior nível nos fornece a altura
◦ Só existe um caminho da raiz para qualquer nó
12
nível 0
nível 1
nível 2
nível 3
Árvores
Árvore cheia ◦ Árvore com número máximo de nós.
◦ Uma árvore de grau d tem número máximo de nós se cada nó, com exceção das folhas, tem grau d.
13
Árvores
a) Quais os nós folhas?
b) Qual o grau de cada nó?
c) Qual o grau da árvore?
d) Liste os ancestrais dos nós B, G e I.
e) Liste os descendentes do nó D.
f) Dê o nível e altura do vértice F.
g) Dê o nível e a altura do vértice A.
h) Qual a altura da árvore ?
14
Árvores Binárias
◦Tipo de árvore em que seus nós possuem no máximo duas sub-árvores
Árvore de grau máximo igual a 2
◦As duas sub-árvores de cada nó são denominadas:
sub-árvore esquerda
sub-árvore direita
Árvore binária completa
◦Árvore em que cada nó possui dois filhos, exceto o nó folha.
15
Árvores Binárias
16
A
B C
D E F
G
Nivel 1
Nivel 2
Nivel 3
Raízes das sub-árvores
Raiz
Esquerda Direita
Grau máximo: 02
Folhas
Árvores Binárias 17
dado
Filho esquerdo Filho direito nó
typedef struct no {
struct no * esq;
t_elemento dado;
struct no * dir;
} t_no;
Árvores Binárias
A maioria das funções de manipulação de árvores são implementadas de forma recursiva
Que operações serão necessárias? Criação/Iniciliazação da árvore Cria nó raiz Árvore vazia Imprimir a árvore Inserir filho esquerdo Inserir filho direito Remover um determinado nó
18
Árvores Binárias
Considerações Uma árvore é representada pelo endereço do nó
raiz Uma árvore vazia é representada pelo valor NULL Algoritmos em C Estrutura da árvore Inicialização Criação de nós Criação de filhos esquerdo e direito Deslocamento para o filho esquerdo ou direito Impressão (exibição) da árvore Pesquisa
19
Árvores Binárias
Percurso ◦Um percurso define a ordem em que os nós de uma árvore serão processados
Tipos de percurso: ◦Pré-ordem Utiliza o nó
Percorre a sub-árvore esquerda
Percorre a sub-árvore direita
◦In-ordem Percorre a sub-árvore esquerda
Utiliza o nó
Percorre a sub-árvore direita
20
Árvores Binárias
◦Pós-ordem
Percorre a sub-árvore esquerda
Percorre a sub-árvore direita
Utiliza o nó
21
A
B C
D E GF
Árvores Binárias 23
void exibirPreOrdem(t_arvore tree)
{
if (tree!=NULL) {
printf("%s ", tree->dado.nome);
exibirPreOrdem(tree->esq);
exibirPreOrdem(tree->dir);
}
}
Árvores Binárias 24
void exibirInOrdem(t_arvore tree)
{
if (tree!=NULL) {
exibirInOrdem(tree->esq);
printf("%s ", tree->dado.nome);
exibirInOrdem(tree->dir);
}
}
Árvores Binárias 25
void exibirPosOrdem(t_arvore tree)
{
if (tree!=NULL) {
exibirPosOrdem(tree->esq);
exibirPosOrdem(tree->dir);
printf("%s ", tree->dado.nome);
}
}
Árvores Binárias 26
void inordem_ (arvore arv) {
pilha p;
arvore aux;
criapilha(&p);
aux = arv;
if (vazia(arv)) return;
do {
while (aux != NULL) {
empilha(&p, aux);
aux = (aux->esq);
}
if (!pilhavazia(p)) {
desempilha(&p, &aux);
printf("%d ", aux->dado);
aux = (aux->dir);
}
} while (!(pilhavazia(p) && aux == NULL));
}
void inordem(arvore arv) {
if (!vazia(arv)) {
inordem(arv->esq);
printf("%d", arv->info);
inordem(arv->dir);
}
}
Árvores binárias 27
// Tipo base dos elementos da arvore
typedef struct elementos {
char nome[100];
} t_elemento;
typedef struct no {
struct no * esq;
t_elemento dado;
struct no * dir;
} t_no;
typedef t_no* t_arvore;
Árvores binárias 28
// Cria um no vazio
t_no * criar ()
{
t_no * no = (t_no*) malloc(sizeof(t_no));
if (no)
no->esq = no->dir = NULL;
return no;
}
Árvores binárias 29
// isVazia - testa se um no eh vazio
int isVazia (t_no * no)
{
return (no == NULL);
}
Árvores binárias 30
t_no * busca(t_arvore tree, t_elemento dado)
{
t_no* achou;
if (tree == NULL)
return NULL;
if (compara(tree->dado, dado)==0)
return tree;
achou = busca(tree->esq, dado);
if (achou == NULL)
achou = busca(tree->dir, dado);
return achou;
}
Árvores binárias 31
// Insere um noh raiz numa arvore vazia.
//Retorna 1 se a insercao for bem sucedida, ou 0 caso contrario.
int insereRaiz(t_arvore* tree, t_elemento dado)
{
t_no* novo;
if (*tree != NULL)
return 0; // erro: ja existe raiz
novo = criar();
if (novo == NULL)
return 0; // erro: memoria insuficiente
novo->dado = dado;
*tree = novo;
return 1;
}
Árvores binárias 32
// Inserir um filho aa direita de um dado noh
int insereDireita(t_arvore tree, t_elemento pai, t_elemento filho)
{
t_no * f, *p, *novo;
// verifica se o elemento ja nao existe
f = busca(tree,filho);
if (f != NULL)
return 0; // erro: dado ja existente
// busca o pai e verifica se ja nao possui filho direito
p = busca(tree,pai);
if (p == NULL)
return 0; // erro: pai nao encontrado
if (p->dir != NULL) return 0; // erro: ja existe filho direito
novo = criar();
if (novo == NULL)
return 0; // erro: memoria insuficiente
novo->dado = filho;
p->dir = novo;
return 1;
}
Árvores binárias 33
// Inserir um filho a esquerda de um dado noh
int insereEsquerda(t_arvore tree, t_elemento pai, t_elemento filho)
{
t_no * f, *p, *novo;
// verifica se o elemento ja nao existe
f = busca(tree,filho);
if (f != NULL)
return 0; // erro: dado ja existente
// busca o pai e verifica se ja nao possui filho esquerdo
p = busca(tree,pai);
if (p == NULL)
return 0; // erro: pai nao encontrado
if (p->esq != NULL) return 0; // erro: ja existe filho esquerdo
novo = criar();
if (novo == NULL)
return 0; // erro: memoria insuficiente
novo->dado = filho;
p->esq = novo;
return 1;
}
Árvores binárias 34
void exibirPreOrdem(t_arvore tree) {
if (tree!=NULL) {
printf("%s ", tree->dado.nome);
exibirPreOrdem(tree->esq);
exibirPreOrdem(tree->dir);
}
}
void exibirInOrdem(t_arvore tree) {
if (tree!=NULL) {
exibirInOrdem(tree->esq);
printf("%s ", tree->dado.nome);
exibirInOrdem(tree->dir);
}
}
void exibirPosOrdem(t_arvore tree){
if (tree!=NULL) {
exibirPosOrdem(tree->esq);
exibirPosOrdem(tree->dir);
printf("%s ", tree->dado.nome);
}
}
Árvores binárias 35
// Exibir a arvore - Procedimento recursivo, usando um percurso pre-ordem.
// sugestao de uso: exibirGraficamente(arvore, 10, 10, 3);
void exibirGraficamente(t_arvore tree, int col, int lin, int desloc)
{
// col e lin sao as coordenadas da tela onde a arvore ira iniciar,
// ou seja, a posicao da raiz, e desloc representa o deslocamento na tela
// (em colunas) de um no em relacao ao no anterior.
if (tree == NULL)
return; // condicao de parada do procedimento recursivo
gotoxy(col,lin);
printf("%s",tree->dado.nome);
if (tree->esq != NULL)
exibirGraficamente(tree->esq,col-desloc,lin+2,desloc/2+1);
if (tree->dir != NULL)
exibirGraficamente(tree->dir,col+desloc,lin+2,desloc/2+1);
}
Árvores binárias 36
void esvaziar(t_arvore *tree)
{
if (*tree == NULL)
return;
esvaziar((&(*tree)->esq));
esvaziar((&(*tree)->dir));
free(*tree);
*tree = NULL;
}
Árvores binárias de pesquisa
1. Todas as chaves da subárvore esquerda são menores que a chave da raiz.
2. Todas as chaves da subárvore direita são maiores que a chave raiz.
3. As subárvores direita e esquerda são também Árvores Binárias de Busca.
37
Árvores binárias de pesquisa
Solução para implementação da busca binária encadeada.
Busca binária sequencial: ◦ Cada comparação na busca binária reduz o número de possíveis candidatos por uma fator de 2. Sendo assim, o número máximo de comparações da chave é aproximadamente log2N.
Árvore binária de pesquisa: ◦ Quando a árvore tem altura mínima possui o mesmo comportamento.
39
Árvores binárias de pesquisa
Implementação: ◦ Mudança nos métodos:
Busca (pesquisa);
Inserção
Remoção
Exibição só in-ordem
40
Árvores binárias de pesquisa 41
t_no * busca(t_arvore tree, t_elemento dado)
{
t_no* achou;
if (tree == NULL)
return NULL;
if (compara(tree->dado, dado)==0)
return tree;
achou = busca(tree->esq, dado);
if (achou == NULL)
achou = busca(tree->dir, dado);
return achou;
}
Árvores binárias de pesquisa 42
t_no * busca(t_arvore tree, t_elemento dado)
{
if (tree == NULL)
return NULL;
if (compara(tree->dado, dado)==0)
return tree;
if (compara(tree->dado, dado)>0)
return busca(tree->esq, dado);
else
return busca(tree->dir, dado);
}
Árvores binárias de pesquisa 43
t_no * buscaSetPai(t_arvore tree, t_elemento dado, t_no ** pai)
{
if (tree == NULL) {
*pai = NULL;
return NULL;
}
if (compara(tree->dado, dado)==0)
return tree;
if (compara(tree->dado, dado)>0) {
*pai = tree;
return buscaSetPai(tree->esq, dado, pai);
}
else {
*pai = tree;
return buscaSetPai(tree->dir, dado, pai);
}
}
Árvores binárias de pesquisa 44
int inserir (t_arvore *tree, t_elemento item)
{
int ok;
// se a raiz for nula, entao insere na raiz
if (*tree == NULL) {
*tree = criar();
if (*tree == NULL)
return 0;
(*tree)->dado = item;
return 1;
}
if (compara((*tree)->dado, item)<0)
ok = inserir (&((*tree)->dir), item);
else
if (compara((*tree)->dado, item)>0)
ok = inserir (&((*tree)->esq), item);
else
ok = 0;
return ok;
}
Árvores binárias de pesquisa
Remoção ◦ Nó a ser removido não possui filhos:
Remove-se o nó e a ligação do pai com o filho é anulada.
◦ Nó a ser removido possui apenas 1 filho:
Remove-se o nó e o pai passa a apontar para o ex-neto, que agora vira filho.
◦ Nó a ser removido possui dois filhos:
Obtém-se o sucessor do nó. O sucessor vai para o lugar do no a ser excluído. O pai do sucessor, aponta agora para o ex-neto. O sucessor->dir passa a apontar para onde o no->dir apontava.
45
Árvores binárias de pesquisa
Sucessor: ◦ Menor nó do conjunto de nós maiores que o nó atual.
Antecessor: ◦ Maior nó do conjunto de nós menores que o nó atual.
47
Árvores binárias de pesquisa 48
int remover (t_arvore *tree, t_elemento item) {
t_no *no, // no aponta para o no a ser removido
*pai, // pai aponta para o pai do no
*sub, // sub aponta que ira substituir o no no
*paiSuce, // pai do no sucessor
*suce; // sucessor do no no
no = *tree; pai=NULL;
no = buscaSetPai(*tree, item, &pai); // procura o no a ser removido, e seta o seu pai.
if (no==NULL)
return 0; // a chave nao existe na arvore, nao conseguiu remover
if (no->esq == NULL) // ver os dois primeiros casos, o no tem um filho no maximo
sub = no->dir;
else {
if (no->dir == NULL)
sub = no->esq;
else { // caso em que o no tem dois filhos
}}
// insere sub na posicao ocupada anteriormente por no
if (pai == NULL) // no eh a raiz, nao tem pai
*tree = sub;
else // verifica se o no eh o filho da esquerda ou da direita
if (no == pai->esq)
pai->esq = sub;
else
pai->dir = sub;
free(no); // libera o no
return 1; // verdadeiro, conseguiu remover
}
Árvores binárias de pesquisa 49
else { // caso em que o no tem dois filhos
paiSuce=no;
sub = no->dir;
suce = sub->esq; // suce eh sempre o filho esq de sub
while (suce != NULL) {
paiSuce = sub;
sub = suce;
suce = sub->esq;
}
// neste ponto, sub eh o sucessor em ordem de no
if (paiSuce != no) {
// no nao e o pai de sub, e sub == paiSuce->esq
paiSuce->esq = sub->dir;
// remove o no sub de sua atual posicao e o
// substitui pelo filho direito de sub
// sub ocupa o lugar de no
sub->dir = no->dir;
}
// define o filho esquerdo de sub de modo que sub
// ocupe o lugar de no
sub->esq = no->esq;
}
Referências
Notas de Aula do Prof. Bruno B. Boniati
Notas de Aula do Prof. João Luís Garcia Rosa
Notas de Aula do Prof. Derzu Omaia
51
Recommended