estrutura de dados -...
TRANSCRIPT
Estruturas de Dados
Deques
Listas recursivas
Árvores (parte 1)
2
Estrututuras de dados lineares
Próximas aulas
Estruturas de dados não lineares
4
Deques
Conjunto de itens onde as eliminações e inserções podem ser realizadas em qualquer das extremidades (início ou fim fila)
Deque (double-ended-queue) ◦ é uma lista linear onde as operações de inserção e remoção podem ser efetuadas tanto no início quanto no final
5
Deques, filas e pilhas 6
Deque Fila
Pilha
Deques
Deque de saída restrita ◦ A remoção só pode ser efetuada ou no início ou no final de uma lista linear
Deque de entrada restrita ◦ A inserção só pode ser efetuada ou no início ou no final de uma lista linear
7
Deques
Dominó
Composição de um trem
Barca para transporte de automóveis ◦ Carros podem entrar e sair por qualquer uma das extremidades.
Escalonamento de tarefas ◦ fila de processos aguardando os recursos do sistema operacional
8
Deques
Operações ◦ Criar Deque
◦ Inserção no inicio
◦ Inserção no fim
◦ Remoção no inicio;
◦ Remoção no fim
◦ Verificar se deque está cheio
◦ Exibir
◦ Esvaziar
9
Deques
Lista duplamente encadeada (duas cabeças)
Lista duplamente encadeada circular
10
PRIM
ULT
D
Listas
Formadas por um conjunto de dados de forma a preservar a relação de ordem linear entre eles
Esses dados são conhecidos como nós
Os nós podem conter dados primitivos ou compostos
Inserção e remoção de elementos em qualquer posição da lista
11
Listas 12
Listas recursivas
Definição recursiva de lista
Uma lista é ◦ Uma lista vazia ou
◦ Um elemento seguido de uma (sub)lista.
13
Listas recursivas 14
// Tipo base dos elementos da lista
typedef struct elementos {
char nome[50];
} t_elemento;
// Estrutura lista
typedef struct no {
t_elemento dado; // elemento contendo os dados
struct no * prox; // ponteiro para o proximo elemento
} t_no, t_lista; // tipo da estrutura
Listas encadeadas 15
t_lista * criar() {
t_lista * lista = (t_lista*) malloc(sizeof(t_lista));
if (lista)
lista->prox = NULL;
return lista;
}
int isVazia(t_lista * lista) {
return (lista == NULL);
}
Listas encadeada 16
/**
* Percorre toda a lista e verifica o tamanho dela.
*/
void exibir(t_lista * lista) {
if (isVazia(lista))
printf("Lista vazia\n");
while (lista != NULL) {
printf("%s\n", lista->dado.nome);
lista = lista->prox;
}
}
Listas recursivas 17
void exibirRec(t_lista * lista) {
if (isVazia(lista))
printf("Lista vazia\n");
else {
printf("%s\n", lista->dado.nome);
exibirRec(lista->prox);
}
}
Listas encadeadas 18
/**
* Percorre toda a lista e verifica o tamanho dela.
*/
int tamanho(t_lista * lista) {
int n = 0;
while (lista != NULL) {
lista = lista->prox;
n++;
}
return n;
}
Listas recursivas 19
/**
* Percorre toda a lista e verifica o tamanho dela.
*/
int tamanhoRec(t_lista * lista) {
if (lista != NULL) {
return 1 + tamanhoRec(lista->prox);
}
else
return 0;
}
Lista encadeada 20
t_no * getNo(t_lista * lista, int pos)
{
// Retorna 0 se posicao invalida. Do contrario, retorna o elemento
int n = 0;
if (pos<0) return 0; // erro: posicao invalida
while (lista != NULL) {
if (n==pos)
return lista;
lista = lista->prox;
n++;
}
return 0; // erro: posicao invalida
}
Listas recursivas 21
t_no * getNoRec(t_lista * lista, int pos, int n)
{
// Retorna 0 se posicao invalida. Do contrario, retorna o
elemento
if (pos<0) return 0; // erro: posica£o invalida
if ((lista != NULL)) {
if (n==pos)
return lista;
else
return getNoRec(lista->prox, pos, n+1);
}
return 0; // erro: posicao invalida
}
Listas recursivas 22
/**
* Retorna a posicao do elemento ou -1 caso nao seja encontrado
*/
int getPosicaoRec(t_lista * lista, t_elemento * dado, int n) {
if (lista != NULL) {
if (compara(&(lista->dado), dado))
return n;
else
return getPosicaoRec(lista->prox, dado, n+1);
}
return -1;
}
Lista encadeada 23
int inserir(t_lista **lista, int pos, t_elemento * dado) {
t_lista * p, * novo;
if (pos == 0) {
novo = criar();
if (novo == NULL) return 0; // erro: memoria insuficiente
novo->dado = *dado;
novo->prox = *lista;
*lista = novo;
return 1;
}
p = getNo(*lista, pos-1);
if (p == NULL) return 0; // erro: posicao invalida
novo = criar();
if (novo == NULL) return 0; // erro: memoria insuficiente
novo->dado = *dado;
novo->prox = p->prox;
p->prox = novo;
return 1;
}
Lista encadeada 24
int remover(t_lista **lista, int pos) {
t_lista *anterior, *p;
if (isVazia(*lista)) return 0; // erro: lista vazia
if (pos<0) return 0; // erro: posicao invalida
// remocao da primeira posicao em lista nao vazia
if (pos == 0) {
p = *lista;
*lista = p->prox;
} else { // remocao em qualquer posicao
anterior = getNo(*lista, pos-1);
if (anterior == NULL) return 0; // posicao invalida
p = anterior->prox;
if (p == NULL) return 0; // erro: posicao invalida
anterior->prox = p->prox;
}
free(p);
return 1;
}
Á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.
25
Árvores
Estrutura não-linear que representa uma relação de hierarquia
Exemplo:
◦Árvore de diretórios;
◦Árvore genealógica.
26
Árvores 27
Presidência
Consultoria Diretoria A Diretoria B
Informática Jurídica
Árvores 28
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
29
( 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
30
Árvores
◦Um nó filho não pode ter dois pais.
◦O filho não sabe quem é o pai.
31
Este exemplo não é árvore! É um grafo.
Árvores 32
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
33
Á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ó
34
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.
35
Á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 ?
36
Á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.
37
Árvores Binárias
38
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 39
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ó
40
Á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
41
Á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
42
Árvores Binárias
◦Pós-ordem
Percorre a sub-árvore esquerda
Percorre a sub-árvore direita
Utiliza o nó
43
A
B C
D E GF
Árvores Binárias 44
A
B C
D E GF
Pré-ordem:
In-ordem
Pós-ordem
ABDECFG
DBEAFCG
DEBFGCA
Árvores Binárias 45
void exibirPreOrdem(t_arvore tree)
{
if (tree!=NULL) {
printf("%s ", tree->dado.nome);
exibirPreOrdem(tree->esq);
exibirPreOrdem(tree->dir);
}
}
Árvores Binárias 46
void exibirInOrdem(t_arvore tree)
{
if (tree!=NULL) {
exibirInOrdem(tree->esq);
printf("%s ", tree->dado.nome);
exibirInOrdem(tree->dir);
}
}
Árvores Binárias 47
void exibirPosOrdem(t_arvore tree)
{
if (tree!=NULL) {
exibirPosOrdem(tree->esq);
exibirPosOrdem(tree->dir);
printf("%s ", tree->dado.nome);
}
}
Árvores Binárias 48
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 49
// 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 50
// 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 51
// isVazia - testa se um no eh vazio
int isVazia (t_no * no)
{
return (no == NULL);
}
Árvores binárias 52
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 53
// 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 54
// 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 55
// 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 56
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 57
// 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 58
void esvaziar(t_arvore *tree)
{
if (*tree == NULL)
return;
esvaziar((&(*tree)->esq));
esvaziar((&(*tree)->dir));
free(*tree);
*tree = NULL;
}
Próximas aulas
Árvore binária de pesquisa
59
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
60