Estrutura de Dados, Lista, Pilha, Fila e Arvores

88
CENTRO FEDERAL DE EDUCAÇÃO TECNOLÓGICA DA PARAÍBA COORDENAÇÃO DO CURSO DE TECNOLOGIA EM TELEMÁTICA APOSTILA DE ESTRUTURA DE DADOS PROF. CÂNDIDO EGYPTO JOÃO PESSOA / PB JULHO / 2003

Transcript of Estrutura de Dados, Lista, Pilha, Fila e Arvores

  • CENTRO FEDERAL DE EDUCAO TECNOLGICA DA PARABA COORDENAO DO CURSO DE TECNOLOGIA EM TELEMTICA

    APOSTILA DE

    ESTRUTURA DE DADOS

    PROF. CNDIDO EGYPTO

    JOO PESSOA / PB JULHO / 2003

  • SUMRIO

    1 INTRODUO .................................................................................. 3

    2 LISTAS ............................................................................................... 4

    3 LISTAS ORDENADAS.................................................................... 16

    4 PILHAS ............................................................................................. 22

    5 FILAS................................................................................................ 27

    6 RVORES ........................................................................................ 34

    7 RVORE BINRIA......................................................................... 39

    8 PESQUISA DE DADOS................................................................... 45

    9 RVORE BINRIA DE PESQUISA .............................................. 49

    10 RVORE AVL............................................................................... 53

    11 INDEXAO................................................................................. 61

    12 HASHING....................................................................................... 63

    13 RVORE-B .................................................................................... 66

    14 CLASSIFICAO DE DADOS .................................................... 74

  • 3

    1 INTRODUO O que uma Estrutura de Dados (ED)?

    Tipos de Dados Estruturas de Dados e Tipos Abstratos de Dados

    Embora estes termos sejam parecidos, eles tm significados diferentes. Em linguagens de programao, o tipo de dados de uma varivel define o conjunto de valores que a varivel pode assumir. Por exemplo, uma varivel do tipo lgico pode assumir o valor verdadeiro ou falso. Uma declarao de varivel em uma linguagem como C ou Pascal especifica:

    1. O conjunto de valores que pode assumir. 2. O conjunto de operaes que podemos efetuar. 3. A quantidade de bytes que deve ser reservada para ela. 4. Como o dado representado por esses bytes deve ser interpretado (por exemplo, uma cadeia de bits pode ser

    interpretada como um inteiro ou real...). Ento, tipos de dados podem ser vistos como mtodos para interpretar o contedo da memria do computador. Mas podemos ver o conceito de Tipo de Dados de uma outra perspectiva: no em termos do que um computador pode fazer (interpretar os bits...) mas em termos do que os usurios desejam fazer (somar dois inteiros...) Este conceito de Tipo de Dado divorciado do hardware chamado Tipo Abstrato de Dado - TAD. Estrutura de Dados um mtodo particular de se implementar um TAD. A implementao de um TAD escolhe uma ED para represent-lo. Cada ED construda dos tipos primitivos (inteiro, real, char,...) ou dos tipos compostos (array, registro,...) de uma linguagem de programao. No importa que tipo de dados estaremos trabalhando, a primeira operao a ser efetuada em um TAD a criao. Depois, podemos realizar incluses e remoes de dados. A operao que varre todos os dados armazenados num TAD o percurso, podendo tambm ser realizada uma busca por algum valor dentro da estrutura.

    Exemplos de TAD:

    Lineares:

    - Listas Ordenadas

    - Pilhas

    - Filas

    - Deques

    No Lineares:

    - rvores

    - Grafos

  • 4

    2 LISTAS So estruturas formadas por um conjunto de dados de forma a preservar a relao de ordem linear entre eles. Uma lista composta por ns, os quais podem conter, cada um deles, um dado primitivo ou composto. Representao:

    L1 L2 L3 . . . Ln-1 Ln

    Sendo:

    L1 1 elemento da lista L2 Sucessor de L1 Ln-1 Antecessor de Ln-1 Ln ltimo elemento da lista. Exemplos de Lista:

    Lista Telefnica Lista de clientes de uma agncia bancria Lista de setores de disco a serem acessados por um sistema operacional Lista de pacotes a serem transmitidos em um n de uma rede de computao de pacotes.

    Operaes Realizadas com Listas:

    Criar uma lista vazia Verificar se uma lista est vazia Obter o tamanho da uma lista Obter/modificar o valor do elemento de uma determinada posio na lista Obter a posio de elemento cujo valor dado Inserir um novo elemento aps (ou antes) de uma determinada posio na lista Remover um elemento de uma determinada posio na lista Exibir os elementos de uma lista Concatenar duas listas

    FORMAS DE REPRESENTAO: a) Seqencial:

    Explora a sequencialidade da memria do computador, de tal forma que os ns de uma lista sejam armazenados em endereos sequenciais, ou igualmente distanciados um do outro. Pode ser representado por um vetor na memria principal ou um arquivo seqencial em disco.

    L pato cabra rato anta macaco . . .

    1 2 3 4 5 6 7 MAX

    b) Encadeada:

    Esta estrutura tida como uma seqncia de elementos encadeados por ponteiros, ou seja, cada elemento deve conter, alm do dado propriamente dito, uma referncia para o prximo elemento da lista.

    Ex: L = pato, cabra, rato, anta, macaco

    patopato macacomacaco antaanta ratorato cabracabra

    LL

  • 5

    2.1 LISTA SEQENCIAL Uma lista representada de forma seqencial um conjunto de registros onde esto estabelecidas regras de precedncia entre seus elementos. O sucessor de um elemento ocupa posio fsica subsequente. A implementao de operaes pode ser feita utilizando array e registro , associando o elemento a(i) com o ndice i (mapeamento seqencial).

    CARACTERSTICAS

    os elementos na lista esto armazenados fisicamente em posies consecutivas;

    a insero de um elemento na posio a(i) causa o deslocamento a direita do elemento de a(i) ao ltimo; a eliminao do elemento a(i) requer o deslocamento esquerda do a(i+1) ao ltimo;

    Mas, absolutamente, uma lista seqencial ou vazia ou pode ser escrita como (a(1), a(2), a(3), ... a(n)) onde a(i) so tomos de um mesmo conjunto A. Alm disso, a(1) o primeiro elemento, a(i) precede a(i+1), e a(n) o ltimo elemento.

    Assim as propriedades estruturadas da lista permitem responder a questes tais como:

    se uma lista est vazia se uma lista est cheia quantos elementos existem na lista

    qual o elemento de uma determinada posio qual a posio de um determinado elemento inserir um elemento na lista

    eliminar um elemento da lista

    Conseqncia:

    As quatro primeiras operaes so feitas em tempo constante. As demais, porm, requerero mais cuidados.

    Vantagem:

    acesso direto indexado a qualquer elemento da lista tempo constante para acessar o elemento i - depender somente do ndice.

    Desvantagem:

    movimentao quando eliminado/inserido elemento tamanho mximo pr -estimado

    Quando usar:

    listas pequenas insero/remoo no fim da lista tamanho mximo bem definido

    Vamos tentar evitar as desvantagens anteriores ao usar endereos no consecutivos (Lista Encadeada).

  • 6

    OPERAES BSICAS A seguir, apresentaremos a estrutura de dados e as operaes bsicas sobre listas, implementadas na linguagem C. Definio da ED: #define MAX ________ /* tamanho mximo da lista */ typedef ______ telem; /* tipo base dos elementos da lista */ typedef struct { telem v[MAX]; /* vetor que contm a lista */ int n; /* posio do ltimo elemento da lista */ } tlista; /* tipo lista */

    tlista

    n v Pato cabra rato anta macaco . . .

    0 1 2 3 4 5 6 MAX-1

    Operaes simples utilizando lista seqencial: 1) Criar uma lista vazia void criar (tlista *L) { L->n = 0; } 2) Verificar se uma lista est vazia int vazia (tlista L) { return (L.n == 0); } 3) Verificar se uma lista est cheia int cheia (tlista L) { return (L.n == MAX); } 4) Obter o tamanho de uma lista int tamanho (tlista L) { return (L.n); } 5) Obter o i-simo elemento de uma lista int elemento (tlista L, int pos, telem *dado) { /* O parmetro dado ir receber o elemento encontrado */ /* Retorna 0 se a posio for invlida. Caso contrrio, retorna 1 */ if ( (pos > L.n) || (pos

  • 7

    6) Pesquisar um dado elemento, retornando a sua posio int posicao (tlista L, telem dado) { /* Retorna a posio do elemento ou 0 caso no seja encontrado */ int i; for (i=1; in == MAX)) || (pos > L->n + 1) ) return (0); for (i=L->n; i>=pos; i--) L->v[i] = L->v[i-1]; L->v[pos-1] = dado; (L->n)++; return (1); } 8) Remoo do elemento de uma determinada posio Requer o deslocamento esquerda dos elementos v(p+1)...v(n) int remover (tlista *L, int pos, telem *dado) { /* O parmetro dado ir receber o elemento encontrado */ /* Retorna 0 se a posio for invlida. Caso contrrio, retorna 1 */ int i; if ( (pos > L->n) || (pos v[pos-1]; for (i=pos; in)-1; i++) L->v[i-1] = L->v[i]; (L->n)--; return (1); }

  • 8

    PRTICA DE LABORATRIO 01. Faa um programa que:

    a) crie uma lista L; b) exiba o seguinte menu de opes:

    EDITOR DE LISTAS

    1 EXIBIR LISTA 2 INSERIR 3 REMOVER 4 EXIBIR ELEMENTO 5 EXIBIR POSIO 6 ESVAZIAR

    ESC SAIR

    DIGITE SUA OPO:

    c) leia a opo do usurio; d) execute a opo escolhida pelo usurio; e) implemente a estrutura de dados LISTA em uma biblioteca chamada L_SEQ (com implementao seqencial e

    usando o inteiro como tipo base), contendo apenas as operaes bsicas de listas (citadas anteriormente);

    f) na opo de exibir lista, devem ser exibidos o tamanho da lista e os seus elementos; g) na opo de insero, deve ser lido o valor do elemento a ser inserido e a posio onde ser efetuada a

    insero;

    h) na opo de remoo, deve ser lido a posio do elemento a ser removido; i) na opo de exibir elemento, deve ser lido a posio do elemento; j) na opo de exibir posio, deve ser lido o valor do elemento; k) as operaes de exibir e esvaziar a lista devem estar inseridas no programa principal; l) aps a execuo de cada opo, o programa deve retornar ao menu para nova opo do usurio ou o

    encerramento do programa (atravs da tecla ESC).

    EXERCCIOS LISTA SEQUENCIAL 01. Inclua, na biblioteca L_SEQ, as funes abaixo especificadas (obs: no faa uso das funes j implementadas).

    a) inserir um dado elemento na primeira posio de uma lista; b) inserir um dado elemento na ltima posio de uma lista; c) modificar um elemento de uma lista, sendo dado a sua posio e o novo valor; d) remover o primeiro elemento de uma lista; e) remover o ltimo elemento de uma lista; f) remover um elemento dado o seu valor.

    02. Modifique o seu programa EDITOR DE LISTAS, adicionando todas as operaes relacionadas na questo anterior.

    03. Escreva um programa que, utilizando a biblioteca L_SEQ, faa o seguinte: a) crie quatro listas (L1, L2, L3 e L4); b) insira sequenciamente, na lista L1, 10 nmeros inteiros obtidos de forma randmica (entre 0 e 99); c) idem para a lista L2; d) concatene as listas L1 e L2, armazenando o resultado na lista L3; e) armazene na lista L4 os elementos da lista L3 (na ordem inversa); f) exiba as listas L1, L2, L3 e L4.

  • 9

    2.2 LISTA ENCADEADA Os elementos da lista so registros com um dos componentes destinado a guardar o endereo do registro sucessor. Ex: L = pato, cabra, rato, anta, macaco

    Cada registro : H duas alternativas para implementao de operaes de listas encadeadas: utilizando arrays estticos ou variveis dinmicas. Entretanto, no h vantagem na implementao da lista encadeada com array, uma vez que permanece o problema do tamanho mximo da lista predefinido e necessita de mais espao para armazenar os endereos dos elementos sucessores. Portanto, vamos utilizar variveis dinmicas como forma de implementao para as nossas listas encadeadas. Antes, importante explicarmos o conceito e utilizao de variveis dinmicas e de ponteiros (apontadores). Variveis Dinmicas e Ponteiros As linguagens de programao modernas tornaram possvel explicitar no apenas o acesso aos dados, mas tambm aos endereos desses dados. Isso significa que ficou possvel a utilizao de ponteiros explicitamente implicando que uma distino notacional deve existir entre os dados e as referncias (endereos) desses dados. A linguagem C utiliza a seguinte notao para a manipulao de ponteiros: tp *P Essa declarao expressa que a varivel P um ponteiro para uma varivel do tipo tp. Valores para ponteiros so gerados quando dados correspondentes a seus tipos so alocados/desalocados dinamicamente. Em C, a funo malloc utilizada para alocar um novo espao de memria e a funo free utilizada para liberar um espao alocado. Esto disponveis na biblioteca . Portanto, deixa -se a cargo do programa (via linguagem de programao), e no do programador, prover e devolver espao para inseres e eliminaes em tempo de execuo. Um ponteiro como P pode assumir o conjunto de valores que correspondem a endereos reais de memria. Por exemplo, sendo tp um registro contendo os campos dado e prox, e sendo P do tipo tp, podemos ter:

    onde o contedo de P corresponderia ao endereo do objeto. Es ses endereos sero as ligaes das listas encadeadas dinmicas. Para designar ponteiro, objeto e campos, a notao utilizada : ponteiro: P objeto: *P campos: (*P).dado ou P->dado (*P).prox ou P->prox Endereo nulo (terra)

    dado dado prox prox

    P

    dado prox

    endereo objeto

    patopato macacomacaco antaanta ratorato cabracabra

    LL

  • 10

    A linguagem C utiliza o valor NULL, disponvel em , para denotar o endereo nulo. Podemos utiliz -lo para atribuies e testes, como nos exemplos abaixo: 1) L = NULL; 2) if (P = NULL) ... Ponteiro x Objeto Apontado Nos exemplos abaixo, ilustrada a diferena entre as operaes de atribuio entre ponteiros (por exemplo, P = Q) e a atribuio entre o contedo das variveis apontadas pelos ponteiros (isto : *P = *Q). Dada a situao abaixo, chamada de (a): Dada a situao (a), aps a atribuio P = Q temos a representao abaixo (b).

    Dada a situao (a), aps a atribuio *P = *Q temos a representao abaixo (c), onde o contedo atribudo. (Lembre-se que *P = *Q equivale s atribuies P->dado = Q->dado e P->prox = Q->prox) Operaes Bsicas com Ponteiros 1) Declarao de Varivel tp *P;

    A varivel do tipo apontador P alocada na memria, com valor indefinido. 2) Criao de um registro p = (tp *) malloc(sizeof (tp));

    a) aloca uma nova varivel do tipo tp (registro) b) atribui o endereo da nova varivel ao ponteiro P

    p1p1 p3p3 p2p2 PP

    q1q1 q3q3 q2q2 QQ

    p1p1 p3p3 p2p2 PP

    q1q1 q3q3 q2q2 QQ

    q1q1 p3p3 p2p2 PP

    q1q1 q3q3 q2q2 QQ

    P

    P

  • 11

    3) Atribuio de contedo ao registro: P->dado = valor;

    4) Atribuio do valor nulo ao campo ponteiro do registro P->prox = NULL; 5) Liberao de um registro free(P);

    Libera o espao apontado por P. conveniente atribuir NULL P para evitar uma utilizao indevida ao endereo apontado por P.

    DEFINIO DA ED #include typedef ____ telem; /* tipo base da lista */ typedef struct no { telem dado; /* campo da informao */ struct no* prox; /* campo do ponteiro para o prximo n */ } tno; /* tipo do n */ typedef tno* tlista; /* tipo lista */ OPERAES SOBRE LISTAS ENCADEADAS 1) Criao da lista vazia void criar(tlista *L) { *L = NULL; } 2) Verificar se a lista est vazia int vazia(tlista L) { return (L == NULL); }

    P

    valor valor

    P

    valor valor

    P

    valor valor

  • 12

    3) Obter o tamanho da lista int tamanho(tlista L) { tlista p = L; int n = 0; while (p != NULL) { p = p->prox; n++; } return n; } 4) Obter o valor do elemento de uma posio dada int elemento(tlista L, int pos, telem *elem) { /* O parmetro elem ir receber o elemento encontrado */ /* Retorna 0 se a posio for invlida. Caso contrrio, retorna 1 */ tlista p = L; int n = 1; if (L == NULL) return 0; /* erro: lista vazia */ while ((p != NULL) && (n < pos)) { p = p->prox; n++; } if (p == NULL) return 0; /* erro: posio invlida */ *elem = p->dado; return 1; } 5) Obter a posio de elemento cujo valor dado int posicao(tlista L, telem valor) { /* Retorna a posio do elemento ou 0 caso no seja encontrado */ if (L != NULL)) { tlista p = L; int n = 1; while (p != NULL) { if (p->dado == valor) return n; p = p->prox; n++; } } return 0; }

  • 13

    6) Inserir um elemento na lista, dado a sua posio int inserir(tlista *L, int pos, telem valor) { /* Retorna 0 se a posio for invlida ou se a lista estiver cheia */ /* Caso contrrio, retorna 1 */ tlista p, novo; int n; /* insero em lista vazia */ if (*L == NULL)) { if (pos != 1) return 0; /* erro: posio invlida */ novo = (tlista) malloc(sizeof(tno)); if (novo == NULL) return 0; /* erro: memria insuficiente */ novo->dado = valor; novo->prox = NULL; *L = novo; return 1; } /* insero na primeira posio em lista no vazia */ if (pos == 1) { novo = (tlista) malloc(sizeof(tno)); if (novo == NULL) return 0; /* erro: memria insuficiente */ novo->dado = valor; novo->prox = *L; *L = novo; return 1; } /* insero aps a primeira posio em lista no vazia */ p = *L; n = 1; while ((n < pos-1) && (p != NULL)) { p = p->prox; n++; } if (p == NULL) return 0; /* erro: posio invlida */ novo = (tlista) malloc(sizeof(tno)); if (novo == NULL) return 0; /* erro: memria insuficiente */ novo->dado = valor; novo->prox = p->prox; p->prox = novo; return 1; }

  • 14

    7) Remover um elemento de uma determinada posio int remover(tlista *L, int pos, telem *elem) { /* O parmetro elem ir receber o elemento encontrado */ /* Retorna 0 se a posio for invlida. Caso contrrio, retorna 1 */ tlista a, p; int n; if (vazia(*L)) return 0; /* erro: lista vazia */ p = *L; n = 1; while ((nprox; n++; } if (p == NULL) return 0; /* erro: posio invlida */ *elem = p->dado; if (pos == 1) *L = p->prox; else a->prox = p->prox; free(p); return(1); }

  • 15

    EXERCCIOS LISTA ENCADEADA

    1) Partindo da situao mostrada no desenho abaixo, e sendo P e Q duas listas encadeadas dinmicas, explique (com

    desenhos) o que acontece nas atribuies seguintes (obs: cada item parte da situao inicial mostrada abaixo) P

    Q

    a) P->prox = Q->prox;

    b) p = Q;

    c) p = NULL;

    d) p = P->prox;

    e) P->dado = (P->prox)->dado

    2) Implemente a estrutura de dados LISTA em uma biblioteca chamada L_DIN (com implementao encadeada). Esta biblioteca deve seguir o mesmo padro da biblioteca L_SEQ desenvolvida no exerccio anterior, ou seja, contendo a mesma interface disponibilizada (definio de tipos, constantes e funes).

    3) Modifique o seu programa Editor de Listas (do exerccio anterior) para que o mesmo utilize a biblioteca L_DIN em

    vez da L_SEQ (como foi feito anteriormente). Execute e teste o programa (ele deve funcionar normalmente). 4) Uma maneira usual de se representar um conjunto pela lista de seus elementos. Supondo esta representao,

    escreva procedimentos para as operaes usuais de conjunto: unio, interseo, diferena e pertinncia.

    p1 p2 p3

    q1 q2 q3

  • 16

    3 LISTAS ORDENADAS So listas lineares onde os elementos esto ordenados segundo um critrio pr-estabelecido. Na realidade, as listas ordenadas diferem das listas genricas pelo fato de que cada novo elemento a ser inserido ocupar uma posio especfica, obedecendo ordenao dos valores j existentes. 3.1 LISTA ORDENADA SEQENCIAL DEFINIO DA ED #define MAX _______ /* tamanho mximo da lista */ typedef _____ telem; /* tipo base dos elementos da lista */ typedef struct {

    telem v[MAX]; /* vetor que contm a lista */ int n; /* posio do ltimo elemento da lista */

    } tlistaord; /* tipo lista ordenada */ OPERAES BSICAS 1) Criar uma lista vazia void criar (tlistaord *L) { L->n = 0; } 2) Verificar se uma lista est vazia int vazia (tlistaord L) { return (L.n == 0); } 3) Verificar se uma lista est cheia int cheia (tlistaord L) { return (L.n == MAX); } 4) Obter o tamanho de uma lista int tamanho (tlistaord L) { return (L.n); } 5) Obter o i-simo elemento de uma lista int elemento (tlistaord L, int pos, telem *dado) { /* O parmetro dado ir receber o elemento encontrado */ /* Retorna 0 se a posio for invlida. Caso contrrio, retorna 1 */ if ( (pos > L.n) || (pos

  • 17

    6) Pesquisar um dado elemento, retornando a sua posio int posicao (tlistaord L, telem dado) { /* Retorna a posio do elemento ou 0 caso no seja encontrado */ int i; for (i=1; in == MAX) return (0); /* erro: lista cheia */ for (i=0; in; i++) { if (L->v[i] == dado) return (0); /* erro: dado j existente */ if (L->v[i] > dado) break; } pos = i; for (i=L->n; i>pos; i--) L->v[i] = L->v[i-1]; L->v[i] = dado; (L->n)++; return (1); } 8) Remoo do elemento de uma determinada posio int remover (tlistaord *L, int pos, telem *dado) { /* O parmetro dado ir receber o elemento encontrado */ /* Retorna 0 se a posio for invlida. Caso contrrio, retorna 1 */ int i; if ( (pos > L->n)) || (pos v[pos-1]; for (i=pos; in)-1; i++) L->v[i-1] = L->v[i]; (L->n)--; return (1); }

  • 18

    3.2 LISTA ORDENADA ENCADEADA DEFINIO DA ED #include typedef ____ telem; /* tipo base da lista */ typedef struct no {

    telem dado; /* campo da informao */ struct no* prox; /* campo do ponteiro para o prximo n */

    } tno; /* tipo do n */ typedef tno* tlistaord; /* tipo lista */ OPERAES BSICAS 1) Criao da lista vazia void criar(tlistaord *L) { *L = NULL; } 2) Verificar se a lista est vazia int vazia(tlistaord L) { return (L == NULL); } 3) Obter o tamanho da lista int tamanho(tlistaord L) { tlistaord p = L; int n = 0; while (p != NULL) { p = p->prox; n++; } return n; } 4) Obter o valor do elemento de uma posio dada int elemento(tlistaord L, int pos, telem *elem) { /* O parmetro elem ir receber o elemento encontrado */ /* Retorna 0 se a posio for invlida. Caso contrrio, retorna 1 */ tlistaord p = L; int n = 1; if (L == NULL) return 0; /* erro: lista vazia */

  • 19

    while ((p != NULL) && (n < pos)) { p = p->prox; n++; } if (p == NULL) return 0; /* erro: posio invlida */ *elem = p->dado; return 1; } 5) Obter a posio de elemento cujo valor dado int posicao(tlistaord L, telem valor) { /* Retorna a posio do elemento ou 0 caso no seja encontrado */ if (L != NULL) { tlistaord p = L; int n = 1; while (p != NULL) { if (p->dado == valor) return n; p = p->prox; n++; } } return 0; } 6) Inserir um dado elemento na lista int inserir(tlistaord *L, telem valor) { /* Retorna 0 se a posio for invlida ou se a lista estiver cheia */ /* Caso contrrio, retorna 1 */ tlistaord atual, ant, novo; int n; novo = (tlistaord) malloc(sizeof(tno)); if (novo == NULL) return 0; /* erro: memria insuficiente */ novo->dado = valor; novo->prox = NULL; ant = NULL; atual = *L; while (atual != NULL && valor >= atual->dado) { if (atual->dado == valor) return 0; /* erro: valor j existente */ ant = atual; atual = atual->prox; }

  • 20

    if (ant == NULL) { novo->prox = *L; *L = novo; } else { ant->prox = novo; novo->prox = atual; } return 1; } 7) Remover um elemento de uma determinada posio int remover(tlistaord *L, int pos, telem *elem) { /* O parmetro elem ir receber o elemento encontrado */ /* Retorna 0 se a posio for invlida. Caso contrrio, retorna 1 */ tlistaord a, p; int n; if (vazia(*L)) return 0; /* erro: lista vazia */ p = *L; n = 1; while ((nprox; n++; } if (p == NULL) return 0; /* erro: posio invlida */ *elem = p->dado; if (pos == 1) *L = p->prox; else a->prox = p->prox; free(p); return(1); }

  • 21

    EXERCCIOS LISTAS ORDENADAS 01. Implemente a TAD Lista Ordenada com representao seqencial em uma biblioteca chamada LORD_SEQ

    (usando como base o tipo string), contendo apenas a estrutura de dados e as operaes bsicas de listas ordenadas (descritas anteriormente).

    02. Implemente a TAD Lista Ordenada com representao encadeada em uma biblioteca chamada LORD_ENC (usando como base o tipo string), contendo apenas a estrutura de dados e as operaes bsicas de listas ordenadas (descritas anteriormente).

    03. Faa um programa que, utilizando qualquer uma das bibliotecas criadas nos itens 01 e 02:

    a) crie uma lista ordenada L; b) exiba o seguinte menu de opes:

    EDITOR DE LISTAS ORDENADAS

    1 EXIBIR LISTA 2 INSERIR 3 REMOVER 4 EXIBIR ELEMENTO 5 EXIBIR POSIO 6 ESVAZIAR

    ESC SAIR

    DIGITE SUA OPO:

    c) leia a opo do usurio; d) execute a opo escolhida pelo usurio; e) na opo de exibir lista, devem ser exibidos o tamanho da lista e os seus elementos; f) na opo de insero, deve ser lido o valor do elemento a ser inserido; g) na opo de remoo, deve ser lido a posio do elemento a ser removido; h) na opo de exibir elemento, deve ser lido a posio do elemento; i) na opo de exibir posio, deve ser lido o valor do elemento; j) as operaes de exibir e esvaziar a lista devem estar inseridas no programa principal; k) aps a execuo de cada opo, o programa deve retornar ao menu para nova opo do usurio ou o

    encerramento do programa (atravs da tecla ESC).

    04. Faa um programa que leia (via teclado) o nome e o sexo de vrias pessoas. Se a pessoa for do sexo masculino,

    insira o seu nome em uma lista chamada MACHOS. Caso contrrio, insira o nome numa lista chamada FEMEAS.

    Ambas as listas devem ser criadas e tm como tipo base o string.

    Escolha uma condio de parada para a entrada de dados a seu gosto.

    Ao final, devem ser exibidas as duas listas criadas.

    OBS: utilize qualquer uma das bibliotecas criadas nos itens 1 e 2.

  • 22

    4 PILHAS Pilhas so listas onde a insero de um novo item ou a remoo de um item j existente se d em uma nica extremidade, no topo.

    Pilha vazia

    Insere (A)

    Insere (B)

    Retira (B)

    Insere (C)

    Retira (C)

    Retira (A) Definio: Dada uma pilha P = ( a(1), a(2), ..., a(n) ), dizemos que a(1) o elemento da base da pilha; a(n) o elemento topo da pilha; e a(i+1) est acima de a(i). Pilhas so tambm conhecidas como listas LIFO (last in first out). Operaes Associadas:

    1. Criar uma pilha P vazia

    2. Testar se P est vazia

    3. Obter o elemento do topo da pilha (sem eliminar)

    4. Inserir um novo elemento no topo de P (empilhar)

    5. Remover o elemento do topo de P (desempilhar)

  • 23

    Implementao de Pilhas Como lista Seqencial ou Encadeada? No caso geral de listas ordenadas, a maior vantagem da alocao encadeada sobre a seqencial - se a memria no for problema - a eliminao de deslocamentos na insero ou eliminao dos elementos. No caso das pilhas, essas operaes de deslocamento no ocorrem. Portanto, podemos dizer que a alocao sequencial mais vantajosa na maioria das vezes. Exemplo do Uso de Pilhas Chamadas de procedimentos Suponha a seguinte situao:

    Quando o procedimento A1 executado, ele efetua uma chamada a A2, que deve carregar consigo o endereo de retorno e1. Ao trmino de A2, o processamento deve retornar ao A1, no devido endereo. Situao idntica ocorre em A2 e A3. Assim, quando um procedimento termina, o seu endereo de retorno que deve ser consultado. Portanto, h uma lista implcita de endereos (e0, e1, e2, e3) que deve ser manipulada como uma pilha pelo sistema, onde e0 o endereo de retorno de A1. No caso de processamento recursivo - por exemplo uma chamada a A2 dentro de A4 - o gerenciamento da lista como uma pilha resolve automaticamente a obteno dos endereos de retorno na ordem apropriada (e0, e1, e2, e3, e4). 4.1 ALOCAO SEQENCIAL DE PILHAS Definio da Estrutura de Dados: #define MAX 10 typedef int telem; typedef struct { telem v[MAX]; int topo; } tpilha;

  • 24

    OPERAES: 1) Criar uma pilha vazia void criar (tpilha *p) { p->topo = -1; } 2) Testar se a pilha est vazia int vazia (tpilha p) { return (p.topo == -1); } 3) Obter o elemento do topo da pilha (sem eliminar) int elemtopo (tpilha p, telem *valor) { if (vazia(p)) return 0; *valor = p.v[p.topo]; return 1; } 4) Inserir um novo elemento no topo da pilha (empilhar) int push (tpilha *p, telem valor) { if (p->topo == MAX-1) return 0; p->v[>topo] = valor; return 1; } 5) Remove o elemento do topo da pilha (desempilhar), retornando o elemento removido int pop (tpilha *p, telem *valor) { if (vazia(*p)) return 0; *valor = p->v[p->topo--]; return 1; } 3.2 ALOCAO DINMICA ENCADEADA DE PILHAS Definio da Estrutura de Dados: #include typedef int telem; typedef struct no{ telem dado; struct no* prox; } tno; typedef tno* tpilha;

  • 25

    OPERAES: 1) Criar uma pilha vazia void criar (tpilha *p) { *p = NULL; } 2) Testar se a pilha est vazia int vazia (tpilha p) { return (p == NULL); } 3) Obter o elemento do topo da pilha (sem eliminar) int elemtopo (tpilha p, telem *elem) { if (vazia(p)) return 0; /* erro: pilha vazia */ *elem = p->dado; return 1; } 4) Inserir um novo elemento no topo da pilha (empilhar) int push (tpilha *p, telem valor) { tpilha novo; novo = (tno *) malloc(sizeof(tno)); if (novo == NULL) return 0; /* erro: memria insuficiente */ novo->dado = valor; novo->prox = *p; *p = novo; return 1; } 5) Remove o elemento do topo da pilha (desempilhar), retornando o elemento removido int pop (tpilha *p, telem *valor) { tpilha aux; if (vazia(*p)) return 0; /* erro: lista vazia */ aux = *p; *valor = (*p)->dado; *p = aux->prox; free(aux); return 1; }

  • 26

    EXERCCIOS PILHAS

    1. Implemente a TAD Pilha com representao seqencial em uma biblioteca chamada P_SEQ (usando como tipo base o char), contendo apenas a estrutura de dados e as operaes bsicas de pilhas (descritas anteriormente).

    2. Implemente a TAD Pilha com representao encadeada em uma biblioteca chamada P_ENC (usando como tipo base o char), contendo apenas a estrutura de dados e as operaes bsicas de pilhas (descritas anteriormente).

    3. Faa um programa que, utilizando qualquer uma das bibliotecas criadas nos itens 1 e 2 : a) crie uma pilha P; b) exiba o seguinte menu de opes:

    EDITOR DE PILHA

    1 EMPILHAR 2 DESEMPILHAR 3 EXIBIR ELEMENTO DO TOPO 4 EXIBIR A PILHA 5 ESVAZIAR A PILHA

    DIGITE SUA OPO:

    c) leia a opo do usurio; d) execute a opo escolhida pelo usurio; e) aps a execuo de cada opo, o programa deve retornar ao menu para nova opo do usurio ou o

    encerramento do programa (atravs da tecla ESC).

    2. Escreva um programa que receba uma linha de texto e use uma pilha para exibir a linha invertida.

    3. Escreva um programa que use uma pilha para determinar se uma string um palndromo (isto , tem a mesma leitura no sentido normal e no sentido inverso). O programa deve ignorar espaos em branco, pontuao e caracteres especiais.

  • 27

    5 FILAS uma lista linear em que a insero feita numa extremidade e a eliminao na outra. Conhecida com estrutura FIFO (First In , First Out). ( a1, a2 , ... , an ) eliminaes inseres no incio no final Exemplos: Escalonamento de "Jobs": fila de processos aguardando os recursos do sistema operacional. Fila de pacotes a serem transmitidos numa rede de comutao de pacotes. Simulao: fila de caixa em banco. Operaes associadas:

    1. Criar - cria uma fila vazia

    2. Vazia - testa se um fila est vazia

    3. Primeiro - obtm o elemento do incio de uma fila

    4. Inserir - insere um elemento no fim de uma fila

    5. Remover - remove o elemento do incio de uma fila, retornando o elemento removido. Implementao de Filas Como lista Seqencial ou Encadeada ? Pelas suas caractersticas, as filas tm as eliminaes feitas no seu incio e as inseres feitas no seu final. A implementao encadeada dinmica torna mais simples as operaes (usando uma lista de duas cabeas). J a implementao seqencial um pouco mais complexa (teremos que usar o conceito de fila circular), mas pode ser usada quando h previso do tamanho mximo da fila. 5.1 IMPLEMENTAO SEQENCIAL DE FILA Definio da Estrutura de Dados: Devido a sua estrutura, ser necessria a utilizao de dois campos que armazenaro os ndices do incio e do final da fila e um vetor de elementos (onde sero armazenados os dados) com tamanho pr-estabelecido. #define MAX ______ /* tamanho mximo da fila */ typedef _____ telem; /* tipo base dos elementos da fila */ typedef struct{ telem v[MAX]; int inicio; /* posio do primeiro elemento */ int final; /* posio do ltimo elemento */ } tfila;

  • 28

    OPERAES COM FILAS: 1. Criar - cria uma fila vazia void criar (tfila *F) { F->inicio = 0; F->final = -1; } 2. Vazia - testa se uma fila est vazia int vazia (tfila F) { return (F.inicio > F.final); } 3. Primeiro - obtm o elemento do incio da fila int primeiro (tfila F, telem *dado) { if (vazia(F)) return 0; /* Erro: fila vazia */ *dado = F.v[F.inicio]; return (1); } 5. Insere - insere um elemento no fim de uma fila int inserir (tfila *F, telem valor) { if (F->final == MAX-1) return 0; (F->final)++; F->v[F->final] = valor; return(1); } 6. Remove - remove o elemento do incio de uma fila, retornando o elemento removido int remover (tfila *F, telem *valor) { if (vazia(*F)) return 0; /* Erro: fila vazia */ primeiro(*F,valor); (F->inicio)++; return(1); }

  • 29

    Problema na implementao seqencial O que acontece com a fila considerando a seguinte seqncia de operaes sobre um fila: I E I E I E I E I E ... (I - insero e E - eliminao) Note que a fila vai se deslocando da esquerda para a direita do vetor. Chegar a condio de "overflow" (cheia), porm estando vazia, ou seja, sem nenhum elemento. Alternativa: No algoritmo de remoo, aps a atualizao de inicio, verificar se a fila ficou vazia. Se este for o caso, reinicializar inicio = 0 e final = -1 Portanto, ficaria: int remover (tfila *F, telem *valor) { if (vazia(*F)) return 0; /* Erro: fila vazia */ primeiro(*F,valor); (F->inicio)++; if (vazia(*F)) { F->inicio = 0; F->final = -1; } return(1); } O que aconteceria se a seqncia fosse: I I E I E I E I E I ... A lista estaria com no mximo dois elementos, mas ainda ocorreria overflow com a lista quase vazia. Alternativa: Forar final a usar o espao liberado por inicio (Fila Circular) FILA CIRCULAR Para permitir a reutilizao das posies j ocupadas, usa-se o conceito de "Fila Circular". Precisamos de um novo componente para indicar quantos elementos existem na fila, no momento. A estrutura de dados com o novo componente ficaria assim representada: #define MAX ______ /* tamanho mximo da fila */ typedef _____ telem; /* tipo base dos elementos da fila */ typedef struct{ telem v[MAX]; int inicio; /* posio do primeiro elemento */ int final; /* posio do ltimo elemento */ int tam; /* nmero de elementos da fila */ } tfila;

  • 30

    As operaes so agora executadas assim: 1. Criar - cria uma fila vazia void criar (tfila *F) { F->inicio = 0; F->final = -1; F->tam = 0; } 2. Vazia - testa se uma fila est vazia int vazia (tfila F) { return (F.tam == 0); } 3. Primeiro - obtm o elemento do incio da fila int primeiro (tfila F, telem *dado) { if (vazia(F)) return 0; /* Erro: fila vazia */ *dado = F.v[F.inicio]; return (1); } 5. Inserir - insere um elemento no fim de uma fila int inserir (tfila *F, telem valor) { if (F->tam == MAX) return 0; (F->tam)++; F->final = (F->final + 1) % MAX; F->v[F->final] = valor; return(1); } 6. Remover - remove o elemento do incio de uma fila, retornando o elemento removido int remover (tfila *F, telem *valor) { if (vazia(*F)) return 0; /* Erro: fila vazia */ primeiro(*F,valor); (F->tam)--; F->inicio = (F->inicio + 1) % MAX; return(1); }

  • 31

    5.2 IMPLEMENTAO ENCADEADA DE FILA Definio da Es trutura de Dados: #include typedef int telem; typedef struct no { telem dado; struct no* prox; } tno; typedef struct fila { tno* inicio; tno* final; } tfila; Operaes: 1. Criar - cria uma fila vazia void criar (tfila *F) { F->inicio = F->final = NULL; } 2. Vazia - testa se uma fila est vazia int vazia (tfila F) { return (F.inicio == NULL && F.final == NULL); }

    inicio final

  • 32

    3. Primeiro - obtm o elemento do incio de uma fila int primeiro (tfila F, telem *elem) { if (vazia(F)) return 0; /* erro: fila vazia */ *elem = (F.inicio)->dado; return 1; } 4. Inserir - insere um elemento no fim de uma fila int inserir (tfila *F, telem valor) { tno *novo; novo = (tno*) malloc(sizeof(tno)); if (novo == NULL) return 0; /* Erro: memria insuficiente */ novo->dado = valor; novo->prox = NULL; if (vazia(*F)) F->inicio = novo; else (F->final)->prox = novo; F->final = novo; return 1; } 5. Remover - remove o elemento do incio de uma fila, retornando o elemento removido int remover (tfila *F, telem *valor) { tno *aux; if (vazia(*F)) return 0; /* Erro: fila vazia */ primeiro(*F,valor); if (F->inicio == F->final) F->final = NULL; aux = F->inicio; F->inicio = (F->inicio)->prox; free(aux); return 1; }

  • 33

    EXERCCIOS FILAS

    1) Implemente o TAD Fila com representao seqencial em uma biblioteca chamada F_SEQ (usando como tipo base o tipo inteiro), contendo apenas a estrutura de dados e as operaes bsicas de filas (descritas anteriormente).

    2) Implemente o TAD Fila com representao encadeada em uma biblioteca chamada F_ENC (usando como tipo base o tipo inteiro), contendo apenas a estrutura de dados e as operaes bsicas de filas (descritas anteriormente).

    3) Faa um programa que, utilizando qualquer uma das bibliotecas criadas nos itens 1 e 2 : a) crie uma fila F; b) exiba o seguinte menu de opes:

    EDITOR DE FILA

    1 INSERIR 2 REMOVER 3 EXIBIR PRIMEIRO ELEMENTO 4 EXIBIR A FILA 5 ESVAZIAR A FILA

    DIGITE SUA OPO:

    c) leia a opo do usurio; d) execute a opo escolhida pelo usurio; e) aps a execuo de cada opo, o programa deve retornar ao menu para nova opo do usurio ou o

    encerramento do programa (atravs da tecla ESC).

    4) Faa um programa que simule o trfego de veculos por um semforo. Considere o seguinte:

    a) O semforo controla duas pistas que se cruzam, e cada uma possui direo nica. b) O semforo possui apenas as luzes verde e vermelha, levando 15 segundos para cada mudana de luz. c) A pista por onde o carro vai (1 ou 2) ser escolhida aleatoriamente. d) Cada carro chegar ao semforo num intervalo (aleatrio) de 1 a 30 segundos aps outro. e) Quando o semforo estiver verde, cada carro da fila leva um temp o de 3 segundos aps a sada do carro da

    frente para dar partida.

    Faa outras consideraes que achar necessrio.

    O programa deve apresentar como sada o movimento no cruzamento a cada instante, mostrando tanto as filas dos carros parados no semforo, como tambm os carros que ainda iro chegar ao cruzamento.

    Sugestes:

    - Inicialmente, gere todos os possveis carros que iro chegar ao semforo, colocando-os em 2 filas: uma para a pista 1 e outra para a pista 2.

    - Os carros podem ser identificados pelo seu tempo previsto de chegada ao cruzamento. - Use filas tambm para os carros que esto parados no semforo. - Apresente na tela o mximo de controles possveis (contadores, flags, etc) que voc estiver usando, para facilitar

    o acompanhamento da simulao.

  • 34

    6 RVORES Nos captulos anteriores estudamos a organizao dos dados de uma maneira linear, onde a propriedade bsica a relao seqencial mantida entre seus elementos. Agora, estudaremos uma outra forma de organizar os dados, chamada genericamente de "no-linear". Essa forma permite que outros tipos de relao entre dados possam ser representados, como por exemplo, hierarquia e composio. Um dos exemplos mais significativos de estruturas no-lineares a rvore. Representao Grfica As trs formas mais comuns de representao grfica de uma rvore so: 1) Representao por parnteses aninhados ( A (B) ( C (D (G) (H)) (E) (F (I)) ) ) 2) Diagrama de incluso

    3) Representao hierrquica

  • 35

    Definio:Definio: Uma rvore T um conjunto finito de elementos denominados ns tais que:

    T = f , e a rvore dita vazia, ou existe um n especial r, chamado raiz de T; os restantes constituem um nico conjunto vazio ou so divididos em

    m 1 conjuntos disjuntos no vazios que so as subrvores de r, cada qual, por sua vez, uma rvore. Notao: Se v um n de T, ento a notao Tv indica a subrvore de T com raiz em v. Subrvore: Seja a rvore acima T = {A, B, C, ...} A rvore T possui duas subrvores: Tb e Tc, onde

    Tb = { B } e Tc = {C, D, ...} A subrvore Tc possui 3 subrvores: Td, Tf e Te, onde

    Td = {D, G, H} Tf = {F, I} Te = {E}

    As subrvores Tb, Te, Tg, Th, Ti possuem apenas o n raiz e nenhuma subrvore. Exemplo: representao da expresso aritmtica: (a + (b * ( (c / d) - e) ) )

    Ns filhos, pais, tios, irmos e av Seja v o n raiz da subrvore Tv de T. Os ns razes w1, w2, ... wj das subrvores de Tv so chamados filhos de v. v chamado pai de w1, w2, ... wj. Os ns w1, w2, ...wj so irmos. Se z filho de w1 ento w2 tio de z e v av de z.

  • 36

    Grau de sada, descendente e ancestral O nmero de filhos de um n chamado grau de sada desse n. Se x pertence subrvore Tv, ento, x descendente de v e v ancestral, ou antecessor, de x. N folha e n interior Um n que no possui descendentes prprios chamado de n folha, ou seja, um n folha aquele com grau nulo. Um n que no folha (isto , possui grau diferente de zero) chamado n interior ou n interno. Grau de uma rvore O grau de uma rvore o mximo entre os graus de seus ns. Floresta Uma floresta um conjunto de zero ou mais rvores. Caminho, comprimento do caminho Uma seqncia de ns distintos v1, v2, ..., vk, tal que existe sempre entre ns consecutivos (isto , entre v1 e v2, entre v2 e v3, ... , v(k-1) e vk) a relao " filho de"ou " pai de" denominada um caminho na rvore. Diz-se que v1 alcana vk e que vk alcanado por v1. Um caminho de vk vrtices obtido pela seqncia de k-1 pares. O valor k o comprimento do caminho. Nvel (ou profundidade) e altura de um n O nvel ou profundidade, de um n v o nmero de ns do caminho da raiz at o n v. O nvel da raiz , portanto, 1. A altura de um n v o nmero de ns no maior caminho de v at um de seus descendentes. As folhas tm altura 1. Nvel da raiz (profundidade) e altura de uma rvore A altura de uma rvore T igual ao mximo nvel de seus ns. Representa-se a altura de T por h(T) e a altura da subrvore de raiz v por h(v). rvore Ordenada

    Uma rvore ordenada aquela na qual os filhos de cada n esto ordenados. Assume-se ordenao da esquerda para a direita. Desse modo a rvore do primeiro exemplo ordenada, mas, a rvore acima no.

  • 37

    rvores Isomrfas Duas rvores no ordenadas so isomrfas quando puderem se tornar coincidentes atravs de uma permutao na ordem das subrvores de seus ns. Duas rvores ordenadas so isomrfas quando forem coincidentes segundo a ordenao existente entre seus ns. rvore Cheia rvore com nmero mximo de ns Uma rvore de grau d tem nmero mximo de ns se cada n, com exceo das folhas, tem grau d. rvore cheia de grau 2: implementao seqencial.

    Array com 7 posies:

    Armazenamento por nvel: posio do n posio dos filhos do n 1 2,3 2 4,5 3 6,7 i (2i,2i+1) Dentre as rvores, as binrias so, sem dvida, as mais comumente utilizadas nas aplicaes em computao.

  • 38

    EXERCCIOS 1. Para a rvore abaixo:

    a) Quantas subrvores ela contm? b) Quais os ns folhas? c) Qual o grau de cada n? d) Qual o grau da rvore? e) Liste os ancestrais dos ns B, G e I. f) Liste os descendentes do n D. g) D o nvel e altura do vrtice F. h) D o nvel e a altura do vrtice A. i) Qual a altura da rvore ?

    2. Dada uma rvore cujo grau da raiz d, como transform-la em uma floresta com d rvores? 3. Dada uma floresta com d rvores como transform -la em u ma rvore cujo n raiz tem grau d? 4. Explique porque a duas rvores abaixo so isomorfas.

    6. Para uma rvore de grau d com nmero mximo de ns, diga:

    Qual o grau dos ns internos da rvore? Qual o grau dos ns folhas? Quantos ns tem a rvore se o grau d e a altura h? Qual a altura da rvore se o grau d e o nmero de ns n?

    7. Para uma rvore cheia de grau d:

    Se um n estiver armazenado na posio i de um array, em que posies estaro seus d filhos? Considerando esta alocao seqencial, quais as conseqncias de inseres e eliminaes na rvore?

  • 39

    7 RVORE BINRIA Uma rvore Binria T um conjunto finito de elementos denominados ns, tal que:

    se T = f, a rvore dita vazia ou existe um n especial r, chamado raiz de T; os restantes podem ser divididos em dois subconjuntos disjuntos, Te

    e Td, que so as subrvores esquerda e direita de r, respectivamente e as quais, por sua vez, tambm so rvores binrias.

    Definio da Estrutura de Dados: typedef int telem; typedef struct no{ struct no* esq; telem info; struct no* dir; } tno; typedef tno* tarvbin;

    Operaes associadas ao TAD rvore binria padro:

    1) Criar uma rvore vazia 2) Verificar se rvore est vazia ou no 3) Buscar um elemento na rvore 4) Inserir um n raiz 5) Inserir um filho direita de um n 6) Inserir um filho esquerda de um n 7) Esvaziar uma rvore 8) Exibir a rvore

    1) Criar uma rvore vazia Define uma rvore vazia e deve ser utilizado antes de qualquer outro. void criar(tarvbin *T) { *T = NULL; } 2) Verifica se rvore vazia ou no Retorna 1 se rvore estiver vazia, 0 caso contrrio. int vazia(tarvbin T) { return (T == NULL); }

  • 40

    3) Buscar um elemento na rvore Busca um elemento na rvore, retornando o seu endereo, caso o encontre. Se o elemento no for encontrado, retorna o endereo nulo (NULL). tarvbin busca(tarvbin T, telem dado) { tarvbin achou; if (T == NULL) return NULL; if (T->info == dado) return T; achou = busca(T->esq, dado); if (achou == NULL) achou = busca(T->dir, dado); return achou; } 4) Inserir um n raiz Insere um n raiz numa rvore vazia. Retorna 1 se a insero for bem sucedida, ou 0 caso contrrio. int ins_raiz(tarvbin *T, telem dado) { tarvbin novo; if (*T != NULL) return 0; /* erro: j existe raiz */ novo = (tno*) malloc(sizeof(tno)); if (novo == NULL) return 0; /* erro: memria insuficiente */ novo->info = dado; novo->esq = novo->dir = NULL; *T = novo; return 1; } 5) Inserir um filho direita de um dado n int ins_dir(tarvbin T, telem pai, telem filho) { tarvbin f, p, novo; /* verifica se o elemento j no existe */ f = busca(T,filho); if (f != NULL) return 0; /* erro: dado j existente */ /* busca o endereo do pai e verifica se j no possui filho direito */ p = busca(T,pai); if (p == NULL) return 0; /* erro: pai no encontrado */ if (p->dir != NULL) return 0; /* erro: j existe filho direito */ novo = (tno*) malloc(sizeof(tno)); if (novo == NULL) return 0; /* erro: memria insuficiente */ novo->info = filho; novo->esq = novo->dir = NULL; p->dir = novo; return 1; } A implementao da operao 6 fica como exerccio para o aluno. A implementao das operaes 7 e 8 sero mostradas mais adiante.

  • 41

    PERCURSO Percorrer uma rvore visitando cada n uma nica vez gera uma seqncia linear de ns, e ento passa a ter sentido falar em sucessor e predecessor de um n segundo um determinado percurso. H trs maneiras recursivas de se percorrer rvores binrias. Travessia em Pr-Ordem

    6. se rvore vazia; fim 7. visitar o n raiz 8. percorrer em pr-ordem a subrvore esquerda 9. percorrer em pr-ordem a subrvore direita

    ABDCEGFHI visita o n quando passar a sua esquerda notao pr-fix

    Travessia em In -Ordem

    1. se rvore vazia, fim 2. percorrer em in-ordem a subrvore esquerda 3. visitar o n raiz 4. percorrer em in-ordem a subrvore direita

    DBAEGCHFI visita o n quando passar embaixo do n notao in-fix

  • 42

    Travessia em Ps-Ordem 1. se rvore vazia, fim 2. percorrer em Ps-Ordem a subrvore esquerda 3. percorrer em Ps-Ordem a subrvore direita 4. visitar o n raiz

    DBGEHIFCA visita o n quando passar a sua direita notao ps-fix

    7) Esvaziar uma rvore Desaloca todo o espao de memria da rvore e retorna a rvore ao estado equivalente ao define, isto , nula. Utiliza o algoritmo de Ps-Ordem para percurso. void esvaziar(tarvbin *T) { if (*T == NULL) return; esvaziar(&(*T)->esq); esvaziar(&(*T)->dir); free(*T); *T = NULL; } 8) Exibir a rvore Um procedimento recursivo para exibir a rvore, usando um percurso pr-ordem, poderia ser o seguinte: void exibir(tarvbin T, int col, int lin, int desloc) { // col e lin so as coordenadas da tela onde a rvore ir iniciar, // ou seja, a posio da raiz, e desloc representa o deslocamento na tela // (em colunas) de um n em relao ao n anterior. if (T == NULL) return; /* condio de parada do procedimento recursivo */ gotoxy(col,lin); printf("%d",T->info); if (T->esq != NULL) exibir(T->esq,col-desloc,lin+2,desloc/2+1); if (T->dir != NULL) exibir(T->dir,col+desloc,lin+2,desloc/2+1); }

  • 43

    Implementao de uma rvore qualquer Como seria a implementao de uma rvore de grau qualquer? Numa rvore qualquer, cada n pode conter um nmero diferente de subrvores. Utilizando alocao encadeada dinmica, uma soluo imediata seria fazer com que o tipo de dado dos ns tenha tantas referncias (ponteiros) quanto o maior nmero de subrvores que um n possa ter (grau da rvore). Com esta organizao h, geralmente, uma grande quantidade de referncias no usadas naqueles ns de grau muito menor do que o mximo previsto. A soluo alternativa mais econmica para este caso transformar a rvore a ser representada em uma rvore binria. Converso em rvore binria Seja T uma rvore qualquer. T convertida em uma rvore binria B(T) da seguinte maneira: B(T) possui um n B(v) para cada n v de T. As razes de T e B(T) coincidem. O filho esquerdo de um n B(v) em B(T) corresponde ao primeiro filho de v em T, caso exista. Se no existir, a

    subrvore esquerda de B(v) vazia. O filho direito de um n B(v) em B(T) corresponde ao irmo de v em T, localizado imediatamente a sua direita, caso

    exista. Se no existir, a subrvore direita de B(v) vazia.

    A

    B C D

    E F G

    A

    B

    C

    D E

    F G

  • 44

    EXERCCIOS 1. Implemente, em um arquivo chamado ARVBIN.LIB, o tipo abstrato de dados Arvore Binria, com alocao

    encadeada dinmica e tendo como base o tipo inteiro. A biblioteca deve conter, alm da estrutura de dados do TAD, as seguintes operaes:

    a) Criar uma rvore vazia

    b) Verificar se rvore est vazia ou no

    c) Inserir um n raiz

    d) Inserir um filho direita de um n

    e) Inserir um filho esquerda de um n

    f) Obter o filho esquerdo de um n

    g) Obter o filho direito de um n

    h) Buscar um elemento na rvore

    i) Buscar o pai de um elemento

    j) Excluir um n (e todos os seus ns descendentes)

    k) Esvaziar uma rvore

    l) Exibir a rvore

    2. Faa um programa que, utilizando a biblioteca ARVBIN.LIB, faa o seguinte:

    a) Crie uma rvore binria T.

    b) Exiba o seguinte menu de opes:

    EDITOR DE RVORE BINRIA

    1 INSERIR RAIZ 2 INSERIR FILHO ESQUERDO 3 INSERIR FILHO DIREITO 4 REMOVER UM N 5 EXIBIR O PAI DE UM N 6 EXIBIR OS FILHOS DE UM N 7 EXIBIR OS ASCENDENTES DE UM N 8 EXIBIR OS DESCENDENTES DE UM N 9 ESVAZIAR A RVORE 0 EXIBIR A RVORE

    DIGITE SUA OPO:

    c) Leia a opo do usurio.

    d) Execute a opo escolhida pelo usurio.

    e) Aps a execuo de cada opo, o programa deve retornar ao menu para nova opo do usurio ou o encerramento do programa (atravs da tecla ESC).

  • 45

    8 PESQUISA DE DADOS

    Iremos agora abordar mtodos para pesquisar grandes quantidades de dados para encontrar determinada

    informao. Conforme veremos, certos mtodos de organizar dados tornam o processo de busca mais eficiente. Como a operao de busca uma tarefa muito comum em computao, o conhecimento desses mtodos de grande importncia para a formao de um bom programador.

    Antes de explicarmos os mtodos, vamos definir alguns termos. Uma tabela ou arquivo um grupo de elementos, cada uma dos quais chamado registro. Existe uma chave associada a cada registro, usada para diferenciar os registros entre si.

    Para todo arquivo, existe pelo menos um conjunto exclusivo de chaves (possivelmente mais). Este tipo de chave chamado de chave primria.

    Entretanto, como todo campo de um registro pode servir como chave em uma determinada aplicao, nem sempre as chaves precisam ser exclusivas. Esse tipo de chave chamado de chave secundria.

    Quanto ao modo de organizao da tabela ou do arquivo, ele pode ser um vetor de registros, uma lista encadeada, uma rvore, etc. Como diferentes tcnicas de busca podem ser adequadas a diferentes organizaes de tabelas, uma tabela freqentemente elaborada com uma tcnica de busca em mente.

    A tabela pode ficar totalmente contida na memria interna, totalmente na memria externa, ou pode ser dividida entre ambas. evidente que so necessrias diferentes tcnicas de pesquisa sob essas diferentes premissas. Neste momento, concentraremos nossos estudos nas tcnicas apropriadas para busca em memria interna.

    8.1 PESQUISA SEQUENCIAL

    Este o mtodo mais simples de pesquisa e consiste em uma varredura serial da tabela, durante a qual o argumento de pesquisa comparado com a chave de cada registro at ser encontrada uma que seja igual, ou ser atingido o final da tabela, caso a chave procurada no se encontre na tabela.

    A seguir apresentado, na linguagem C, o algoritmo de pesquisa seqencial em uma tabela no ordenada. O desempenho deste algoritmo bastante modesto, j que o nmero mdio de comparaes para a localizao de uma chave arbitrria em uma busca de sucesso dado por (n+1)/2, considerando que todas as entradas possuem a mesma probabilidade de serem solicitadas. No caso de um busca sem xito sero necessrias n comparaes.

    Adotaremos para a nossa implementao uma funo que recebe como parmetro um vetor de elementos inteiros (v), a quantidade de elementos do vetor (n) e a chave a ser pesquisada (k), retornando o endereo (ndice) do elemento encontrado, ou 1 caso contrrio.

    int busca_sequencial (int v[], int n, int k) { int i; for (i=0; i

  • 46

    int busca_sequencial_com_sentinela (int v[], int n, int k) { int i; k[n] = k; for (i=0; k != v[i]; i++); if (i < n) return i; else return 1; }

    Reordenando a lista para obter mais eficincia

    Existem vrias aplicaes onde nem todas as entradas possuem a mesma probabilidade de serem solicitadas. Em certas situaes, um registro que foi ultimamente acessado pode ter maior probabilidade de um novo acesso do que os demais. Sendo assim, seria til ter um algoritmo que reordenasse continuamente a tabela de modo que os registros mais acessados fossem deslocados para o incio, enquanto os acessados com menos freqncia fossem deslocados para o final.

    Existem dois mtodos de busca que fazem essa operao: o mtodo mover-para-frente e a transposio.

    No mtodo mover-para-frente, sempre que uma pesquisa obtiver xito, o registro recuperado ser removido de sua atual posio da lista e colocado no incio da lista. Obviamente esse mtodo s ser eficiente se aplicado sobre uma tabela organizada como uma lista encadeada.

    O contra-argumento do mtodo mover-para-frente que uma nica recuperao no implica necessariamente que o registro ser recuperado freqentemente; posiciona-lo no incio da tabela reduzir a eficincia da busca de todos os outros registros que o precediam anteriormente. Se avanarmos um registro somente uma posio sempre que ele for recuperado, garantiremos que ele avanar para o incio da lista apenas se recuperado com freqncia.

    Nesse mtodo, chamado de transposio, um registro recuperado com sucesso trocado pelo registro imediatamente anterior.

    Outra vantagem deste mtodo em relao ao mtodo mover-para-frente, que ele pode ser aplicado com eficincia sobre tabelas armazenadas em vetores e sobre tabelas armazenadas em listas encadeadas.

    8.2 PESQUISA EM TABELA ORDENADA

    Se a tabela estiver armazenada em ordem crescente ou decrescente de chaves de registros, vrias tcnicas podero ser empregadas para aumentar a eficincia da operao de busca.

    Uma vantagem evidente ao pesquisar uma tabela ordenada em comparao a uma tabela no-ordenada no caso em que a chave do argumento est ausente da tabela. No caso de um arquivo no-ordenado, so necessrias n comparaes para detectar esse fato. No caso de uma tabela ordenada, presumindo-se que as chaves dos argumentos estejam uniformemente distribudas pela faixa de chaves na tabela, s sero necessrias n/2 comparaes (em mdia). Isso acontece porque sabemos que determinada chave est ausente numa tabela armazenada em ordem crescente de chaves assim que encontramos uma chave maior que o argumento. Na realidade, essa mdia de n comparaes, considerando que a cada elemento acessado so feitas duas comparaes de chave.

    A busca seqencial em tabela ordenada pode ser assim implementada:

    int busca_sequencial_ordenada (int v[], int n, int k) { int i; for (i=0; (i=v[I]); i++) if (k == v[I]) return i; return 1; }

  • 47

    Devido simplicidade e eficincia do processamento seqencial sobre tabelas ordenadas, talvez compense classificar um arquivo antes de pesquisar chaves dentro dele. Isto se verifica principalmente nas situaes onde a maior parte das transaes de busca a registros, com pouqussimas inseres e remoes na tabela.

    8.3 PESQUISA BINRIA

    A pesquisa binria um mtodo que pode ser aplicado a tabelas ordenadas, armazenadas em dispositivos de acesso direto.

    O mtodo consiste na comparao do argumento de pesquisa (k) com a chave localizada no meio da tabela. Se for igual, a pesquisa termina com sucesso. Se k for maior, o processo repetido para a metade superior da tabela e se for menor, para a metade inferior.

    Assim, a cada comparao, a rea de pesquisa reduzida metade do nmero de elementos. O nmero mximo de comparaes ser, portanto, igual a aproximadamente log2 n para a localizao de uma entrada ou para a constatao de que ela no est presente na tabela. Na realidade, 2*log2 n, porque, em C, fazemos duas comparaes de chave de cada vez por meio da repetio.

    Apesar de que a pesquisa binria pode ser mais bem definida recursivamente, possvel que a sobrecarga associada recursividade a torne inadequada para uso em algumas situaes. A seguir, apresentado o algoritmo de pesquisa binria, numa verso no-recursiva.

    int busca_binria (int v[], int n, int k) { int low=0, hi=n-1, mid; while (low

  • 48

    8.5 CONCLUSES

    - A busca binria possui desempenho melhor que os outros dois mtodos.

    - Para o pior caso (da busca sem sucesso), os mtodos seqencial e seqencial ordenado possuem, praticamente, o mesmo desempenho.

    - A busca binria e a seqencial ordenada requerem um esforo adicional para classificar o vetor de pesquisa.

    - Para vetores pequenos, os trs mtodos assemelham-se; medida que N cresce, as diferenas no nmero mdio de comparaes cresce assustadoramente (veja tabela a seguir).

    NMERO MDIO DE COMPARAES PARA BUSCA BEM SUCEDIDA

    TAMANHO DO VETOR

    ( N )

    PESQ. BINRIA 2 * LOG2N

    PESQ. SEQ. ORDENADA

    N / 2

    PESQ. SEQUENCIAL

    N

    2 2 1 2

    4 4 2 4

    8 6 4 8

    16 8 8 16

    32 10 16 32

    64 12 32 64

    1024 20 512 1024

    EXERCCIOS:

    1. Qual a vantagem da utilizao de um dummy (sentinela) em um algoritmo de busca seqencial?

    2. Em algumas situaes, a busca seqencial se torna mais eficiente utilizando-se as tcnicas de "mover-para-frente" e de "transposio". Que situaes seriam essas e qual a diferena entre estas duas tcnicas?

    3. Faa um programa que gere, aleatoriamente, um vetor V de 10 elementos inteiros, leia um valor inteiro K e, usando a pesquisa seqencial, verifique se existe algum elemento de V que seja igual a K.

    4. Escreva uma nova verso para o mtodo da busca seqencial, acrescentando a ele o mtodo da transposio.

    5. Faa um programa que gere um vetor V contendo os 10 primeiros nmeros inteiros mltiplos de 5, leia um valor inteiro K e, usando a pesquisa binria, verifique se existe algum elemento de V que seja igual a K.

    6. Escreva uma verso recursiva para o mtodo da pesquisa binria.

    7. Faa uma anlise comparativa, do ponto de vista de desempenho, dos 3 mtodos de pesquisa apresentados.

  • 49

    9 RVORE BINRIA DE PESQUISA Uma rvore Binria de Pesquisa T (ABP) ou rvore Binria de Busca tal que T = f e a rvore dita vazia ou seu n raiz contm uma chave e: 1. Todas as chaves da subrvore esquerda so menores que a chave da raiz. 2. Todas as chaves da subrvore direita so maiores que a chave raiz. 3. As subrvores direita e esquerda so tambm rvores Binrias de Busca.

    Num algoritmo de busca a medida de eficincia dada pelo nmero de comparaes necessrias para se localizar uma chave, ou descobrir que ela no existe. Numa lista linear com n chaves, temos que, no pior caso, faremos n comparaes. O nmero de comparaes cresce linearmente em funo do nmero de chaves. BUSCA BINRIA Pesquisa realizada se informao est arma zenada de forma ordenada e em seqncia (em um array). Qual a eficincia do algoritmo de busca binria? Cada comparao na busca binria reduz o nmero de possveis candidatos por uma fator de 2. Sendo assim, o nmero mximo de comparaes da chave aproximadamente log2 n. Com busca binria obtemos a melhor eficincia possvel em array, mas, ficamos limitados representao seqencial (deslocamentos, previso de memria, etc). Podemos utilizar a rvore Binria de Pesquisa e obteremos o mesmo desempenho anterior (desde que a altura seja mnima). a caracterstica de altura mnima que garante que estamos tomando a chave do meio da poro pesquisada. Para garantir a performance tima temos que garantir que a rvore seja balanceada durante a construo e que o balanceamento seja mantido em inseres e eliminaes (rvore AVL). ESTRUTURA DE DADOS

    typedef _____ telem; /* tipo base dos elementos da rvore */

    typedef struct no { struct no *esq; telem info; struct no *dir; } tno; /* n da rvore */

    typedef tno *tabp; /* ponteiro para a raiz da rvore */

  • 50

    OPERAES BSICAS:

    1. Criao 2. Testar se est vazia 3. Busca 4. Insero 5. Remoo 6. Exibio 7. Esvaziar

    1. Criao A criao de uma ABP se d de forma idntica a de uma rvore binria padro, ou seja, incializando a varivel que aponta para a raiz da rvore com o valor nulo. void criar (tabp *T) { *T = NULL; } 2. Testar se est vazia Tambm esta operao no tem nenhuma diferena ao que foi visto na rvore binria comum. A condio para a ABP estar vazia se a varivel que aponta para a raiz contm o valor nulo. int vazia (tabp T) { return (T == NULL); } 3. Busca Funo que procura um smbolo em uma rvore binria de pesquisa. Se o smbolo procurado estiver presente na rvore, a funo retorna o endereo do n que o contm. Caso contrrio, o valor retornado o endereo nulo. Torna-se mais eficiente se implementada com percurso pr-ordem. tabp busca(tabp T, telem dado) { if (T == NULL) return NULL; if (T->info == dado) return T; if (T->info > dado) return busca(T->esq, dado); else return busca(T->dir, dado); } 4. Insero Os novos elementos inseridos em uma rvore entram sempre na condio de folhas. O processo de insero desenvolve-se da seguinte forma:

    a) Se a rvore for vazia, o smbolo instalado na raiz e a funo retorna o valor 1.

    b) Caso contrrio, instalado na sub-rvore da esquerda, se for menor que o smbolo da raiz, ou da direita, se for maior.

    c) Se for igual ao smbolo da raiz, o elemento no ser inserido na rvore e a funo retornar o valor 0.

  • 51

    int inserir (tabp *T, telem item) { int ok; if (*T == NULL) { *T = (tno *) malloc(sizeof(tno)); if (*T == NULL) return 0; (*T)->esq = NULL; (*T)->dir = NULL; (*T)->info = item; return 1; } if ((*T)->info < item) ok = inserir (&((*T)->dir), item); else if ((*T)->info > item) ok = inserir (&((*T)->esq), item); else ok = 0; return ok; }

    5. Excluso Para a remoo de um n de uma rvore deve-se levar em considerao que seus filhos devem continuar na rvore e esta dever continuar ordenada (menor esquerda, maior direita). E ento camos em trs possibilidades que devem ser tratadas separadamente, a fim de manter a estrutura da rvore binria. Sendo elas: a) Quando o n a ser excludo no contenha filhos:

    O n simplesmente removido. b) Quando o n a ser excludo contenha somente um dos filhos:

    O pai do n a ser excludo passa a apontar para este filho e o n removido. c) Quando o n a ser excludo contenha os dois filhos:

    Busca-se o maior elemento da sub-rvore da esquerda (a partir da raiz da sub-rvore esquerda caminha-se sempre para a direita at achar um n cujo filho direita nulo). Transfere-se a informao deste n para o n a ser removido e remove-se este novo n, que cair no caso (a) ou (b).

    Vamos deixar a cargo do aluno, como exerccio, a implementao desta rotina. 6. Exibio Para exibir todos os smbolos da rvore de forma a se preservar a ordenao, deve-se utilizar o percurso in-ordem.

    void exibir (tabp T) { if (T != NULL) { exibir (T->esq); printf ("%d ",T->info); exibir (T->dir); } } 7. Esvaziar Para remover todos os elementos da uma ABP, deve-se utilizar o percurso ps-ordem. Esta operao implementada da mesma forma que na rvore binria padro.

    void esvaziar(tabp *T) { if (*T == NULL) return; esvaziar(&(*T)->esq); esvaziar(&(*T)->dir); free(*T); *T = NULL; }

  • 52

    EXERCCIO 1. Implemente, em um arquivo chamado ABP.LIB, o tipo abstrato de dados ABP (rvore binria de pesquisa), com

    alocao encadeada dinmica e tendo como base o tipo inteiro. A biblioteca deve conter, alm da estrutura de dados do TAD, as seguintes operaes:

    a) Criar uma rvore binria de pesquisa vazia

    b) Verificar se uma rvore est vazia ou no

    c) Inserir um novo elemento

    d) Remover um elemento, dado o seu valor

    e) Verificar se a uma rvore contm um dado valor, retornando o endereo do n, caso encontrado, ou nil, caso contrrio.

    f) Exibir todos os valores de uma rvore

    g) Esvaziar uma rvore

    2. Faa um programa que, utilizando a biblioteca ABP.LIB, faa o seguinte:

    a) Crie uma rvore binria de pesquisa T.

    b) Exiba o seguinte menu de opes:

    EDITOR DE RVORE BINRIA DE PESQUISA

    1 INSERIR 2 REMOVER 3 PESQUISAR 4 EXIBIR A RVORE 5 ESVAZIAR A RVORE

    DIGITE SUA OPO:

    c) Leia a opo do usurio.

    d) Execute a opo escolhida pelo usurio.

    e) Aps a execuo de cada opo, o programa deve retornar ao menu para nova opo do usurio ou o encerramento do programa (atravs da tecla ESC).

  • 53

    10 RVORE AVL RVORE BALANCEADA Sucessivas inseres e remoes de ns em uma rvore binria de pesquisa fazem com que existam diferenas entre os nveis das suas folhas, o que acarreta grandes diferenas de performance no acesso a seus ns. No pior caso, se os dados j esto ordenados, a rvore ser uma lista, perdendo toda a eficincia da busca. Nesses casos, diz-se que a rvore ficou degenerada. Uma rvore dita balanceada quando, para qualquer n, as suas sub-rvores esquerda e direita possuem a mesma altura. Isso equivale a dizer que todos os ponteiros nulos esto no mesmo nvel, ou seja, que a rvore est completa. Isso nem sempre possvel, por conta do nmero de ns da rvore. Um critrio mais simplificado seria considerar uma rvore balanceada se o nmero mximo de ns da sub-rvore esquerda diferencie no mximo 1 da sub-rvore direita. BALANCEAMENTO ESTTICO Consiste em construir uma nova verso de uma rvore binria de pesquisa, reorganizando-a. Essa reorganizao possui duas etapas: 1. Percurso in-ordem sobre a rvore, para gerar um vetor ordenado com o contedo de todos os seus ns. 2. Criao de uma ABP a partir desse vetor, da seguinte forma:

    a) Identifica-se o n mdio do vetor, que passa a ser considerado a raiz da ABP que est sendo criada. b) Cada uma das metades do vetor tratada analogamente, de modo que cada n intermedirio ser a raiz de uma

    sub-rvore.

    BALANCEAMENTO DINMICO: AVL Em 1962, dois matemticos russos (Adelson-Velskii e Landis) definiram uma nova estrutura para balanceamento de rvores ABP e deram o nome de AVL. Esta nova estrutura permite o balanceamento dinmico da rvore e com boa performance. Fator de Balanceamento (FB) de um n a altura da subrvore direita do n menos a altura da subrvore esquerda do n. Insero AVL: O que pode acontecer quando um novo n inserido numa rvore balanceada? Na rvore abaixo:

    Ns 9 ou 11 podem ser inseridos sem balanceamento. Subrvore com raiz 10 passa a ter uma subrvore e subrvore

    com raiz 8 vai ficar melhor balanceada. Insero dos ns 1, 3, 5 ou 7 requerem que a rvore seja rebalanceada.

  • 54

    Seja T uma rvore AVL na qual sero feitas inseres de novos ns. Para que a rvore se mantenha AVL (ABP e balanceada), preciso efetuar operaes de rebalanceamento quando necessrio. A idia consiste em verificar, aps cada incluso, se algum n ficou desbalanceado, isto , se a diferena de altura entre as 2 sub-rvores ficou maior do que 1. Em caso afirmativo, aplicam-se as transformaes apropriadas para rebalance-lo. Torna-se ento necessrio que a estrutura dos ns da rvore contenha mais um campo (bal) alm dos j existentes (ESQ, INFO e DIR). Este novo campo armazenar o Fator de Balanceamento (FB) de cada n. Veja abaixo como ser a estrutura de dados para uma rvore AVL: typedef _____ telem; typedef struct no { struct no *esq; telem info; int bal; struct no *dir; } tno; typedef tno *tavl; Sero utilizadas 4 transformaes, indicadas nas figuras dos casos abaixo. Observe que as subrvores T1, T2, T3 e T4 podem ser vazias ou no. O ponteiro T aponta para o n raiz p, que o n raiz da transformao. Observe que a ordem dos ns no percurso in-ordem preservada em todas as rotaes , e portanto possvel aplicar qualquer nmero de rotaes sobre uma rvore para torn-la balanceada, sem alterar a ordem dos ns. Ou seja, as transformaes preservam a rvore como sendo ABP. Vamos analisar o que pode ocorrer durante o processo de insero em uma rvore AVL - e a vai ficar claro o contexto em que estas transformaes so aplicadas. Suponha que um n q foi inserido em uma rvore AVL T. Se, aps a insero todos os ns de T permanecem balanceados, a rvore continua AVL. Caso contrrio, seja p o n mais prximo s folhas de T que ficou desbalanceado (ou seja, o ancestral mais jovem que ficou desbalanceado). Observe que no h ambigidade na escolha de p, pois qualquer sub-rvore de T que se tornou desbalanceada aps a incluso de q deve necessariamente conter p. Logo, p se encontra no caminho entre q e a raiz de T, e sua escolha nica. Sejam hE(p) e hD(p) as alturas das sub-rvores esquerda e direita de p, respectivamente. Naturalmente, |hE(p)-hD(p)| > 1. Ento, conclui-se que |hE(p)-hD(p)| = 2, pois T era uma rvore AVL e a insero de um nico n no pode aumentar a altura de qualquer sub-rvore em mais do que 1. As 4 situaes possveis a serem analisadas so: Caso 1: hE(p) > hD(p) Seja q o n que foi inserido na sub-rvore esquerda de p. Alm disso, sabe-se que p possui um filho esquerdo u diferente de q (caso contrrio, p no teria ficado desbalanceado). Finalmente, por esse mesmo motivo, sabe-se que hE(u) diferente de hD(u). Neste caso, so duas situaes possveis :

    Caso 1.1: hE(u) > hD(u) Esta situao est ilustrada na fig 1(a), sendo q um n pertencente a T1. Observe que h(T1) - h(T2) = 1 e h(T2) = h(T3).

    fig.1: rotao para a direita

  • 55

    void rot_dir (tavl *T) { tavl u; u = (*T)->esq; (*T)->esq = u->dir; u->dir = *T; (*T)->bal = 0; *T = u; } Conseqentemente, uma rotao direita da rvore com raiz p transforma a sub-rvore considerada na rvore da fig.1(b), o que restabelece o balanceamento da sub-rvore em p e, conseqentemente, da sub-rvore T. Caso 1.2: hD(u) > hE(u) Neste caso, u possui um filho direito v, e a situao ilustrada na fig.2(a), sendo que o modulo de h(T2)- h(T3) menor que 1 e o mximo de h(T2) e h(T3) = h(T1)=h(T4).

    fig.2: rotao para a esquerda e em seguida para a direita

    void rot_esq_dir (tavl *T) { tavl u,v; u = (*T)->esq; v = u->dir; u->dir = v->esq; v->esq = u; (*T)->esq = v->dir; v->dir = *T; if (v->bal == -1) (*T)->bal = 1; else (*T)->bal = 0; if (v->bal == 1) u->bal = -1; else u->bal = 0; *T = v; }

    Aplica-se ento a rotao esquerda_direita raiz p. A nova configurao da rvore aparece em 2(b), e o balanceamento restabelecido.

    Caso 2: hD(p) > hE(p) Ou seja, q foi inserido na subrvore direita de p. Dessa vez, sabe-se que p possui um filho direito u diferente de q (caso contrrio, p no teria ficado desbalanceado). Segue que hE(u) diferente de hD(u), e as duas situaes possveis so:

    Caso 2.1: hD(u) > hE(u) Este caso corresponde ao da fig.3(a), sendo q pertencente a T3. As relaes de altura so h(T3) - h(T2) = 1 e h(T2) = h(T1).

  • 56

    fig.3: rotao para a esquerda

    void rot_esq (tavl *T) { tavl u; u = (*T)->dir; (*T)->dir = u->esq; u->esq = *T; (*T)->bal = 0; *T = u; } Isso significa que a rotao esquerda torna a rvore novamente AVL (fig 3(b)). Caso 2.2: hE(u) > hD(u) Ento, u possui o filho esquerdo v (fig.4(a)). Observe que as alturas da sub-rvores T1, T2, T3 e T4 satisfazem as mesmas relaes do caso 1.2. Dessa forma, a aplicao da rotao direita-esquerda torna a rvore novamente AVL (fig.4(b)), ao balancear a sub-rvore com raiz p.

    fig.4: rotao para a direita e em seguida para a esquerda

    void rot_dir_esq (tavl *T) { tavl u,v; u = (*T)->dir; v = u->esq; u->esq = v->dir; v->dir = u; (*T)->dir = v->esq; v->esq = *T; if (v->bal == 1) (*T)->bal = -1; else (*T)->bal = 0; if (v->bal == -1) u->bal = 1; else u->bal = 0; *T = v; }

    fcil concluir que o rebalanceamento de p implica tambm no rebalanceamento de seu n pai, pois a transformao aplicada diminui em 1 a altura da sub-rvore com raiz em p. Isso assegura o rebalanceamento de todos os ns ancestrais de p, portanto uma nica transformao resulta no rebalanceamento de T.

  • 57

    Implementao do procedimento de insero Seja T uma rvore AVL, e x a chave a ser includa em algum novo n q. O processo pode ser descrito do seguinte modo: Primeiro, busca a chave x. Se ela j est na rvore, nada precisa ser feito. Caso contrrio, a busca determina a posio de insero da nova chave, e a insero feita. A seguir, verifica se a insero desbalanceou algum n. Em caso negativo, a insero termina. Caso contrrio, feito o rebalanceamento de T, atravs de uma das operaes de rotao vistas, dependendo do caso. Para verificar se algum n de T ficou desbalanceado, vamos utilizar o campo bal existente na estrutura de cada n. Esse campo armazena, para cada n v, o valor hD(v) - hE(v). O n est balanceado se -1 v->bal 1. O problema como atualizar este campo de forma eficiente, tendo em vista a incluso de q. Se q for inserido na subrvore esquerda de v, e esta incluso ocasionar um aumento na altura desta subrvore, subtrai-se 1 de v->bal. Se esse valor ficar igual a -2, ento v ficou desbalanceado. Analogamente, se q for inserido na subrvore direita de v e provocar um aumento na sua altura, adiciona-se 1 a v ->bal, e o n ficar desbalanceado se o valor resultante for 2. Para completar o processo, precisamos identificar os casos em que a incluso de q provoca um aumento na altura h(v) da subrvore Tv. Inicialmente, observamos que a insero do n q acarreta obrigatoriamente uma alterao na subrvore esquerda ou direita de seu n pai w. O campo balano permite avaliar se esta alterao pode ou no se propagar aos outros ns v que esto no caminho entre w e a raiz da rvore. Suponha que o n q foi inserido na subrvore esquerda de v. A anlise se inicia com v = w e prossegue em seus ns ancestrais, de forma recursiva. O processo encerrado quando se acha a raiz de uma sub-rvore Tv. que no foi modificada. Trs situaes distintas podem ocorrer: Caso A: v->bal=1 antes da incluso. Neste caso, v.bal passa a ser 0, e a altura da subrvore de raiz v no foi modificada. Conseqentemente, os balanos dos ns no caminho entre v e a raiz no foram alterados. Caso B: v->bal=0 antes da incluso. Neste caso, v->bal passa a ser -1, e a altura da subrvore de raiz v foi modificada. Conseqentemente, os ns no caminho de v at a raiz podem ter seus balanos modificados, e devem ser analisados. Se v a raiz de T, a anlise se encerra, pois nenhum n ficou desbalanceado. Caso contrrio, deve-se repetir o processo substituindo v pelo seu pai. Caso C: v->bal=-1 antes da incluso. Neste caso, v->bal passa a ser -2, e o n ficou desbalanceado. A rotao correta deve ser aplicada. Qualquer rotao implica em que a subrvore resultante tenha a mesma altura da subrvore antes da incluso. As alturas dos ancestrais de v no precisam ser avaliadas. Para uma insero na subrvore direita de v, casos simtricos devem ser considerados. A rotina de busca e insero dada a seguir. A chamada externa ins_avl (&T.item), onde:

    T o endereo da raiz da rvore; item a chave a ser inserida; O retorno ser 1 se a insero tiver sucesso ou 0 caso contrrio e servir como um auxiliar para propagar a

    verificao do FB. Foram criadas duas rotinas auxiliares, caso1 e caso2 , para facilitar a implementao. void caso1(tavl *T) { /* item foi inserido esquerda de T e causa desbalanceamento FB(T)=-2 */ tavl u; u = (*T)->esq; if (u->bal == -1) rot_dir(&(*T)); /* Caso 1.1 sinais iguais e negativo */ else rot_esq_dir(&(*T)); /* Caso 1.2 sinais diferentes */ (*T)->bal = 0; }

  • 58

    void caso2(tavl *T) { /* item foi inserido direita de T e causa desbalanceamento FB(T)=2 */ tavl u; u = (*T)->dir; if (u->bal == 1) rot_esq(&(*T)); /* Caso 2.1 sinais iguais e positivo */ else rot_dir_esq(&(*T)); /* Caso 2.2 sinais diferentes */ (*T)->bal = 0; } int ins_avl(tavl *T, telem item) { int ok; if (*T == NULL) { *T = (tno *) malloc(sizeof(tno)); if (*T == NULL) return 0; (*T)->esq = NULL; (*T)->dir = NULL; (*T)->info = item; (*T)->bal = 0; return 1; } if ((*T)->info > item) { /* recursividade esquerda */ ok = ins_avl (&((*T)->esq), item); if (ok) switch ((*T)->bal) { /* prxima raiz a se verificar o FB */ case 1 : (*T)->bal = 0; /* era mais alto direita, fica com FB=0 */ ok = 0; /* interrompe propagao de balanceamento */ break; case 0 : (*T)->bal = -1; /* ficou com a esquerda maior e propaga FB */ break; case -1: caso1(&(*T)); /* FB(p)=-2 */ ok = 0; /* nao propaga mais */ break; } } else if ((*T)->info < item) { /* recursividade direita */ ok = ins_avl (&((*T)->dir), item); if (ok) switch ((*T)->bal) { /* prxima raiz a se verificar o FB */ case -1: (*T)->bal = 0; /* era mais alto esquerda, fica com FB=0 */ ok = 0; /* interrompe propagao de balanceamento */ break; case 0 : (*T)->bal = 1; /* ficou com a direita maior e propaga FB */ break; case 1 : caso2(&(*T)); /* FB(p)=2 */ ok = 0; /* nao propaga mais */ break; } } else ok = 0; return ok; }

  • 59

    Remoo em rvore AVL A retirada de um valor de uma rvore Binria de Busca mais complexa do que a insero, porque, enquanto qualquer insero sempre ocorre numa folha da rvore, uma retirada envolve pelo menos dois casos distintos: os ns com dois filhos e os demais ns. Nas rvores AVL essa dificuldade permanece. Mtodo manual de remoo de rvore AVL Inicialmente, faz-se a retirada do n, usando o algoritmo de busca e retirada de uma ABP. Se no desbalanceou, o processo est encerrado. Se desbalanceou a rvore, isto , se um ou mais ns ficou com |FB(n)| > 1, raciocina-se em termos de insero, perguntando: se o desbalanceamento ocorresse devido a uma insero, que n teria sido inserido para causar tal desequilbrio? Identificado o n, simula-se sua insero e faz-se a rotao necessria. Exemplo 1: dada a rvore abaixo(a), retirando o 5 resulta uma rvore desbalanceada no n 10(b). A partir da, raciocina-se como se estivssemos inserindo: que n inserido teria causado esse desequilbrio? o 30. Aplicando os conhecimentos de insero em rvore AVL, constata-se que uma rotao simples esquerda resolve o problema. O resultado est na AVL (c).

    Exemplo 2: Retirando a folha 12 de (a), na figura abaixo, desbalanceia a raiz (b); supondo-se a insero recente de 8, corrige-se o desequilbrio mediante uma rotao dupla esquerda (c).

    Caso especial : Seja a rvore AVL (a), no desenho abaixo. A retirada da folha 2 desbalanceia a raiz 6 (b). Todavia, essa configurao jamais poderia resultar de uma seqncia de inseres, pois, se ela fosse 8, 12 ou 12, 8, a primeira dessas incluses provocaria rotao. Soluo: escolhe-se arbitrariamente um desses dois ns, despreza-se o outro (mantendo-o na rvore, obviamente), e simula-se a sua insero. Escolhemos o 12, que exige uma operao mais simples: rotao simples esquerda. O resultado a rvore AVL (c).

    Se escolhssemos o 8, desprezando o 12, a rotao exigida seria dupla direita, mas chegaramos, tambm, a um resultado satisfatrio (experimente). Infelizmente, h situaes mais complexas, onde o prprio processo de balanceamento devido a retirada de um n de uma subrvore, por reduzir a altura desta, pode provocar um novo desequilbrio na rvore a qual ela pertence. Ento, no resta outra sada seno reaplicar o mtodo para a rvore que desbalanceou. E novo desequilbrio pode ser provocado mais acima, na estrutura, exigindo novo balanceamento. E assim por diante, at que toda a rvore volte a ser uma A VL.

  • 60

    EXERCCIO 1. Implemente, em um arquivo chamado AVL.LIB, o tipo abstrato de dados rvore AVL, com alocao encadeada

    dinmica e tendo como base o tipo inteiro. A biblioteca deve conter, alm da estrutura de dados do TAD, as seguintes operaes:

    a) Criar uma rvore AVL vazia

    b) Verificar se uma rvore est vazia ou no

    c) Inserir um novo elemento

    d) Remover um elemento, dado o seu valor

    e) Verificar se a uma rvore contm um dado valor, retornando o endereo do n, caso encontrado, ou nil, caso contrrio.

    f) Exibir todos os valores de uma rvore

    g) Esvaziar uma rvore

    2. Faa um programa que, utilizando a biblioteca AVL.LIB, faa o seguinte:

    a) Crie uma rvore AVL T.

    b) Exiba o seguinte menu de opes:

    EDITOR DE RVORE AVL

    1 INSERIR 2 REMOVER 3 PESQUISAR 4 EXIBIR A RVORE 5 ESVAZIAR A RVORE

    DIGITE SUA OPO:

    c) Leia a opo do usurio.

    d) Execute a opo escolhida pelo usurio.

    e) Aps a execuo de cada opo, o programa deve retornar ao menu para nova opo do usurio ou o encerramento do programa (atravs da tecla ESC).

  • 61

    11 INDEXAO

    Para reduo do tempo de acesso a um registro o mais indicado o uso de ndice para orientar a busca. Por este motivo, um dos importantes componentes de um arquivo o diretrio que abriga uma coleo de um ou mais ndices do arquivo.

    Um ndice um mapeamento que associa a cada chave, uma referncia ao registro que a contm. Assim, dada uma chave de pesquisa, o ndice fornece imediatamente a localizao do registro correspondente. Resumindo, ndice um par (chave, endereo).

    Quando trabalhamos com chave primria, seus valores tero sempre valores nicos e no nulos de forma a identificar unicamente cada registro. Porm, comum a existncia de valores chaves (no chaves primrias, mas simplesmente chaves) que se repetem, como por exemplo, num arquivo de funcionrios, a chave funo certamente ter repeties. Mesmo com repeties de valores de chaves o uso de ndices no ser eliminado, ser apenas manipulado de maneira mais complexa.

    Um arquivo que utiliza a estrutura de ndices composto, basicamente, por duas partes: um arquivo de dados e uma estrutura de ndice associada. A cada insero de um novo registro no arquivo de dados, inserido no ndice uma referncia a ele. Para acessar um determinado registro, para consulta ou alterao, feita a pesquisa na estrutura de ndice para que sua localizao no disco seja encontrada e, em seguida, feito o acesso direto para leitura ou gravao.

    Arquivo de Dados Estrutura de ndice

    CHAVE REGISTRO CHAVE ENDEREO

    0 Joo ... Antonio 2

    1 Maria ... Beto 5

    2 Antonio ... Carlos 4

    3 Rosa ... Francisco 6

    4 Carlos ... Joo 0

    5 Beto ... Maria 1

    6 Francisco ... Rosa 3

    O objetivo melhorar o desempenho no processo de incluso de novos registros, pesquisa do arquivo e atualizao do arquivo.

    No havendo movimentao dentro do arquivo, a eficincia na incluso pode ser facilmente obtida se os registros forem inseridos sempre no final do arquivo. Porm, a pesquisa ser extremamente lenta, uma vez que os registros no esto armazenados ordenados e a busca ser realizada atravs de uma leitura seqencial dos re