estrutura de dados -...

52
Estrutura de Dados Carlos Eduardo Batista Centro de Informática - UFPB [email protected]

Upload: duongdiep

Post on 08-Feb-2019

225 views

Category:

Documents


0 download

TRANSCRIPT

Estrutura de Dados Carlos Eduardo Batista

Centro de Informática - UFPB

[email protected]

Estruturas de Dados

Árvores (parte 2)

2

Á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 5

Presidência

Consultoria Diretoria A Diretoria B

Informática Jurídica

Á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 10

raiz

A B C D …

Sub-árvores do

nó raiz

Filhos de C Filhos de A

Á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 22

A

B C

D E GF

Pré-ordem:

In-ordem

Pós-ordem

ABDECFG

DBEAFCG

DEBFGCA

Á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 38

Á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 46

Á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;

}

Próximas aulas

Árvores binárias de pesquisa

Árvores balanceadas

Heap

50

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

Estrutura de Dados Carlos Eduardo Batista

Centro de Informática - UFPB

[email protected]