You are on page 1of 147

Algoritmo e Estrutura de Dados

Docente: Msc Monitores: Amndio de Jesus Almada Manuel Ebo Dikiefu Fabiano,

Sumrio
1.

Introduo 1.1 Tipo de dados Abstracto -TDA 1.2 Ponteiros 1.3 Funes 1.4 Estruturas e tipos de dados definidos pelo programador

2.

Listas Lineares 2. 1 Listas Sequenciais 2.2 Listas ligadas 2.3 Listas Circulares 2.4 Listas duplamente ligadas 2.5 Pilhas 2.5.1Implementao sequencial da pilha 2.5.2Implementao dinmica da pilha 2.6 Filas 2.6.1Implementao sequencial da fila 2.6.2Implementao dinmica da fila

3.

rvores

3.1 Introduo
3.2 Representao de rvores 3.2 Definio 3.3 Terminologia

3.4 rvores binrias


3.5 rvores balanceadas 3.6 Definio da estrutura de dados
4.

Ordenao

4.1 Ordenao por Seleco


4.2 Ordenao por Insero 4.3 Shellsort 4.4 Quicksort 4.5 Heapsort
5.

Pesquisa 5.1 Pesquisa Sequencial 5.2 Pesquisa Binria 5.3 rvores de Pesquisa

Capitulo I: Introduo
Sumrio:
1.1 Tipo de Dado Abstracto (TDA) 1.2 Ponteiros 1.3 Funes 1.4 Estruturas 1.5 Tipos de dados definidos pelo programador

1.1 Tipo de Dados Abstracto - TDA

Tipos de Dados Tipo de dados de uma variveis define o conjunto de valores que a varivel pode assumir. Especifica a quantidade de bytes que deve ser reservadas a ela. Tipos de dados podem ser vistos como mtodos para interpretar o contedo da memria do computador 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 o utilizador desejam fazer (somar dois inteiros)

1.1 Tipo de Dado Abstracto TDA

Tipo de Dado Abstracto


Agrupa a estrutura de dados juntamente com as operaes que podem ser feitas sobre esses dados O TDA encapsula a estrutura de dados. Os utilizadores do TDA s tem acesso a algumas operaes disponibilizadas sobre esses dados Utilizador do TDA x Programador do TDA Utilizador s enxerga a interface, no a sua implementao Dessa forma, o utilizador pode abstrair da implementao especfica. Qualquer modificao nessa implementao fica restrita ao TDA A escolha de uma representao especfica fortemente influenciada pelas operaes a serem executadas

1.1 Tipo de Dado Abstracto - TDA

Estrutura de Dados - ED um mtodo particular de se implementar um TDA Cada ED construda dos tipos primitivos (inteiro, real, char, ) ou dos tipos compostos (array, registo, ) de uma linguagem de programao. Algumas operaes realizadas em TDA so: criao, incluso, busca e remoo de dados.

Exemplos de TDA
Listas Lineares: Listas ligadas Pilhas Filas Listas no Lineares: . rvores . Grafos

1.2 Apontadores

Na realidade, C permite que o programador referencie a posio de objectos bem como os prprios objectos (isto , o contedo de suas posies)

1.2 Apontadores

O objectivo:
Armazenar o endereo de outra varivel.

Sintaxe
tipo * ptr
ptr - o nome da varivel do tipo apontador tipo o tipo da varivel para qual apontar. * - indica que uma varivel do tipo apontador

Exemplo 1:
char *pc; int *pi; float *pf

1.2 Apontadores

Um ponteiro como qualquer outro tipo de dado em C. O valor de um ponteiro uma posio de memria da mesma forma que o valor de um inteiro um nmero. Os valores dos ponteiros podem ser atribudos como qualquer outro valor. A converso de pf do tipo ponteiro para um nmero de ponto flutuante para o tipo ponteiro para um inteiro pode ser feita escrevendo-se: pi = (int *) pf; Se pi um ponteiro para um inteiro, ento pi +1 o ponteiro para o inteiro imediatamente seguinte ao inteiro *pi em memria, pi1 o ponteiro para o inteiro imediatamente anterior a *pi.

Apontadores
Por exemplo, suponha que determinada mquina usa endereamento de bytes, que um inteiro exija 4 bytes e que valor de pi seja 100 (isto , pi aponta para inteiro *pi na posio 100). Sendo assim, o valor de pi1 96, o valor de pi+1 104. De modo semelhante, se o valor da varivel pc 100 e um caractere tem um byte, pc 1 refere-se a posio 99 e pc+1 posio 101.

Apontadores
Uma rea na qual os ponteiros de C desempenham um notvel papel passagem de parmetro para funes. Normalmente, os parmetros so passados para uma funo em C por valor, isto , os valores sendo passados so copiados nos parmetros da funo de chamada no momento em que a funo for chamada. Se o valor de um parmetro for alterado dentro da funo, o valor no programa chamada no

func(int y) {

++y;
printf(%d\n, y); } void main(){

int x =5;
printf(%d\n, x) // imprime 5 func(x); } // imprime 6 printf(%d\n, x) // imprime 5

Apontadores Passagem por referncia


Se precisar usar func para modificar o valor de x, precisaremos passar o endereo de x.

void func(int *py) {


++(*py); printf(%d\n, *py); } void main(){

int x =5; printf(%d\n, x); // imprime 5 func(&x); // imprime 6 printf(%d\n, x); // imprime 6 }

1.2.1 Inicializao automtica de apontadores

Um bom hbito para evitar problemas de programao a inicializao sempre dos apontadores A constante simblica NULL, quando colocada num apontador, indica que este no aponta para nenhuma varivel.
int x = 5; float pi = 3.1415; int px = &x; float * ppi = π

1.3 Funes

Conceito:
uma unidade autnoma de cdigo do programa e desenhada para cumprir uma tarefa particular.

Sintaxe
tipo nome_da_funo (parametros){ comandos; }

1.3 Funes

Quando uma funo no retorna um valor para funo que a chamou ela declarada como void. Ex:
#include<stdio.h>
void linha (char ch, int size){ int i; for (i=1; i size; i++) putchar(ch); } main(){ char lin; int tamanho; printf(Introduza o tipo de linha); scanf(%c, &lin); printf(Introduza o tamanho da linha); scanf(%d, &tamanho); linha(lin, tamanho); getchar(); }

1.3 Funes
1.3.3 Funo com retorno

O tipo de retorno tem que ser declarada.

Ex1 : int potencia (int base, int expoente){ int i, pot=1; If (expoente<0) return; for (i=1; i <= expoente; i++) pot=pot*base; Return pot; }

main(){ int b, e; printf(Introduza a base e expoente); scanf(%d%d, &b, &e); printf(valor=%d\n, potencia(b,e)); getchar(); }

Estrutura de dados e tipo de dados definido pelo utilizador


Estruturas so peas contguas de armazenamento que contm vrios tipos simples agrupados numa entidade. Estrutura so manifestadas atravs da palavra reservada struct seguida pelo nome da estrutura e por uma rea delimitada por colchetes que contm campos. struct pessoa { char nome[30]; int idade; char sexo; }; Para criao de novos tipos de estrutura de dados utiliza-se a palavra-chave: typedef
typedef struct pessoa { char nome[30]; int idade; char sexo; } PESSOA;

Estrutura
Para obter directamente o valor de um campo, a forma <nome_da_estrutura>.<campo>. Para obter o valor de um campo apontado por um ponteiro a forma <nome_da_estrutura> -> <campo>

Estruturas Recursivas

Estruturas podem ser recursivas, i.e., podem conter ponteiros para si mesmas. Esta peculiaridade ajuda a definir estruturas do tipo lista.

struct lista{ struct lista *prox; int dado; }

Capitulo II: Listas


Sumrio
2. Listas Lineares 2. 1 2.2 2.3 2.4 2.5 Listas Sequenciais Listas ligadas Listas Circulares Listas duplamente ligadas Pilhas 2.5.1 Implementao sequencial da pilha 2.5.2 Implementao dinmica da pilha Filas 2.6.1 Implementao sequencial da fila 2.6.2 Implementao dinmica da fila 2.6.3 Fila circular

2.6

Listas

Listas so uma coleco de objectos, onde os itens podem ser adicionados em qualquer posio arbitrria.

Listas
Propriedades Uma lista pode ter zero ou mais elementos Um novo item pode ser adicionado a lista em qualquer ponto Qualquer item da lista pode ser eliminado Qualquer item da lista pode ser acessado

Listas Lineares - Formas de Representao

Sequencial Explora a sequencialidade da memria do computador, de tal forma que os ns de uma lista sejam armazenados em endereo sequencias. Podem ser representado por um vector na memria principal ou um arquivo sequencial em disco.
L

Ado
0

Adelin Basto Daniel Josefin a 1a 2 s 3 4 5

MAX-1

Ligada Esta estrutura tida como uma sequencia de elementos ligados por ponteiros, ou seja, cada elemento deve conter, alm do dado propriamente dito, uma referencia para o prximo elemento da lista.
Ado Adelina Bastos Daniel Josefina

Listas sequenciais
Uma lista representada de forma sequencial um conjunto de registos onde esto estabelecidas regras de precedncia entre seus elementos. Implementao de operaes pode ser feita utilizando array e registo, associando o elemento a[i] com o ndice i. 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 a[i] ao ltimo. A eliminao do elemento a[i] requer o deslocamento esquerda do a[i+1] ao ltimo.

Listas Sequenciais

Vantagens Acesso directo a qualquer dos elementos da lista. Tempo constante para aceder o elemento i - (depende somente de ndice) Desvantagens Para inserir ou remover elementos temos que deslocar muitos outros elementos Tamanho mximo pr-estimado Quando usar Listas pequenas Insero e remoo no fim da lista Tamanho mximo bem definido

Listas sequenciais
Operaes bsicas

Criar uma lista vazia


Verificar se uma lista est vazia Verificar se uma lista est cheia

Obter o tamanho de uma lista Obter o i-simo elemento de uma lista Pesquisar um dado elemento, retornando a sua posio. Insero de um elemento em uma determinada posio Remoo do elemento de uma determinada posio

Listas Ligadas

Uma lista ligada ou lista encadeada uma estrutura de dados linear e dinmica. Ela composta por clulas que apontam para o prximo elemento da lista. Para "ter" uma lista ligada/encadeada, basta guardar seu primeiro elemento, e seu ltimo elemento aponta para uma clula nula. Vantagens
A insero ou a retirada de um elemento na lista no implica na mudana de lugar de outros elementos; No necessrio saber, anteriormente, o nmero mximo de elementos que uma lista pode ter, o que implica em no desperdiar espao na Memria Principal (Memria RAM) do computador.

Desvantagens
manipulao torna-se mais "perigosa" uma vez que, se o encadeamento (ligao) entre elementos da lista for mal feito, toda a lista pode ser perdida; Para acessar o elemento na posio n da lista, deve-se percorrer os n - 1 anteriores.

Listas ligadas

As listas ligadas so conjunto de elementos ligados, onde cada elemento contem uma ligao com um ou mais elementos da lista. Uma lista ligada tem necessariamente uma varivel ponteiro apontando para o seu primeiro elemento. Essa varivel ser utilizada sempre, mesmo que esteja vazia, e dever apontar sempre para o incio da lista. Cada elemento da lista ligada ser composta por duas/trs partes principais: uma parte conter informaes e a(s)

Listas Ligadas - Propriedades


Uma lista pode ter zero ou mais elementos Um novo item pode ser adicionado a lista em qualquer ponto Qualquer item da lista pode ser eliminado Qualquer item da lista pode ser acessado.

Listas Ligadas
Uma lista ligada composta por elementos alocadas em memria. Utiliza-se duas funes para alocar (desalocar) memria dinamicamente:

malloc() free()

Ex:
L = (lista) malloc(sizeof(lista)); free(L);

Lista Simplesmente Ligada


Cada elemento da lista possui uma nica conexo. Assim, cada elemento ser formado por um bloco de dados e um ponteiro para prximo elemento. Chamaremos cada elemento n. Um n um conjunto de informaes de tipos diferentes, portanto ela uma estrutura definida pelo programador: struct no{ int dado; struct no *prox; //ponteiro para o prximo

Listas Simplesmente Ligadas

Sendo que uma memria alocada dinamicamente no possui o nome como no caso das variveis declaradas no programa, toda lista dever ter uma varivel que apontar para o seu primeiro elemento.
struct no *head; //cabea da lista head=null; //inicia a lista com nulo

Listas Simplesmente Ligadas


Podemos nos referenciar a uma lista ligada atravs do seu primeiro n. Entretanto, toda vez que realizarmos uma insero no incio da lista teremos que garantir que todas as partes do programa que referenciam a lista tenha suas referencias actualizadas. Para evitar este problema, comum usar um n especial chamado n cabea, e um ponteiro para o primeiro elemento da lista.

Listas Simplesmente Ligadas


A implementao em C de uma lista simplesmente ligada com n cabea. Precisamos de dois tipos de dados:

Um tipo n, representa cada elemento da lista. e um tipo lista que contm um ponteiro para n cabea.

Listas Simplesmente Ligadas


typedef struct no{ int dado; // o dado poderia ser de qualquer tipo struct no *prox; // ponteiro para prximo
elemento

} NO
typedef struct lista{ NO *head; }LISTA

Listas Simplesmente Ligadas

Podemos querer 3 tipos de inseres em uma lista:


no incio, no fim, ou em uma posio arbitrria.

Na lista simplesmente ligada a insero no incio trivial: Fazemos o prximo do novo elemento apontar para o primeiro elemento da lista e fazemos com que o novo elemento seja o primeiro elemento (cabea) da lista. A insero em posio arbitrria pode ser feito em tempo constante se tivermos um ponteiro para o elemento que ser o elemento anterior ao novo elemento: void insere_apos(NO *novo, NO *anterior){ novo->prox = anterior->prox; antrior->prox = novo; }

A insero no final da lista exige que encontremos seu ltimo elemento.

Listas Simplesmente Ligadas

Remoo de um elemento arbitrrio requer que encontremos o elemento anterior ao removido.

void remove(LISTA *lista, NO *no){ NO *ant; if(no == lista->head){ lista->head = no->prox; } else{ ant = anterior(lista, no) if(ant!=NULL) ant->prox = no->prox; } free(no); }

Listas duplamente ligadas

Listas duplamente ligadas so usadas frequentemente nos problema que exija uma movimentao eficiente desde um n quer para o seu antecessor, quer para o seu sucessor.
valor valor valor NULL Atrs

frent e NULL

Listas duplamente ligadas


Uma das virtudes de listas duplamente ligadas, o facto de nos podemos deslocar para o n anterior ou para n seguinte, a um n dado com facilidade. Uma lista duplamente ligada permitenos remover um n qualquer da lista em tempo constante, usando apenas um ponteiro para essa clula.

Listas duplamente ligadas operaes


anterior(): devolve o elemento anterior a um determinado elemento. cria(): devolve uma lista com um elemento insere(): insere um determinado elemento na lista. mostra(): mostra o contedo da lista. procura(): devolve a posio de um determinado elemento. remove(): remove o elemento desejado da lista.

Lista Circular Ligada

As listas circulares so do tipo simples ou duplamente ligadas, em que o n final da lista a ponta para o n inicial. Este tipo de estrutura adapta-se a situaes do tipo FIFO (First In First Out).

Fig. 1 Lista circular ligada ( lista simplesmente ligada)


dado dado dado dado

Fig. 2 Lista circular ligada ( lista duplamente ligada)

dado

dado

dado

dado

Pilha e Fila
1.

Pilha
Implementao sequencial da pilha Operaes bsicas Implementao sequencial da fila

2.

Fila
I. II.

Pilhas
Pilhas so listas onde a insero ou a remoo de um item feita no topo. Definio: dada a 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 conhecidas como listas LIFO (last in first out)

Implementao de pilhas

Como lista sequencial ou ligada?


No caso geral de listas ordenadas, a maior vantagem da alocao dinmica sobre a sequencial, se a memria no for problema, a eliminao de deslocamento na insero ou eliminao dos elementos. No caso das pilhas, essas operaes de deslocamento no ocorrem. Portanto, alocao sequencial mais vantajosa na maioria das vezes. Como o acesso a pilha limitado ao ltimo elemento inserido, todos os elementos do meio do vector devem estar preenchidos. H desvantagens quando a quantidade de dados a ser armazenados no conhecido

Pilha

Implementao Sequencial
#define MAXPILHA 50 #define tpilha(p) (p->topo p->base) typedef struct pilha{ int base[MAXPILHA]; int *topo; }PILHA;

Implementao Sequencial
1.

Inicializa a pilha vazia int inicializa(PILHA *pp){ pp->topo = pp->base; return 1; } Verifica se pilha est vazia
int vazia(PILHA *pp){ return (pp->topo == pp->base) ? 1 : 0; }

2.

3.

Coloca dado na pilha. Apresenta erro se no haver espao. int push(PILHA *pp, int dado){ if(tpilha(pp) == MAXPILHA) return 0; *pp->topo=dado; pp->topo++; return 1; }

Implementao Sequencial
4.

Retira o valor do topo da pilha a atribui a dado. int pop (PILHA *pp, int dado){ if(vazia(pp) == 1) return 0; pp->topo--; *dado = *pp->topo return 1; } Retorna o valor do topo da pilha se remov-lo. int topo(PILHA *pp, int *dado){ if(pop(pp, dado) == 0) return 0; return push (pp, dado)
}

5.

Fila

Uma Fila uma estrutura de dados do tipo FIFO (First In First Out), cujo funcionamento inspirado no de uma fila natural, na qual o primeiro elemento a ser inserido sempre o primeiro elemento a ser retirado.

Fila
Uma fila tem por norma as seguintes funcionalidade:

Colocar e retirar dados da fila:


insere guardar um elemento na fila remove retirar um elemento da fila topo retornar o elemento do topo da fila.

Testar se a fila est vazia ou cheia:


cheia Verificar se a fila est cheia (no pode guardar mais elementos). vazia Verificar se a fila est vazia (no contm elementos)

Inicializar ou limpar:
inicializa Colocar a fila num estado pronto a ser utilizada

Fila Outras Operaes


Buscar por elementos que coincidam com certo padro (casamento de padro) Ordenar uma lista Intercalar duas listas Concatenar duas listas Dividir uma lista em duas Fazer cpia da lista

Fila

A implementao de uma fila pode ser efectuada atravs da utilizao de diferentes estruturas de dados (vectores, listas ligadas, rvores, etc.). De seguida, apresenta-se duas implementao de uma fila atravs da utilizao de vectores e listas ligadas.

Fila

Caractersticas das filas:


Os dados so armazenados pela ordem de entrada

Tipos de filas:
Filas de espera (queues)
com duplo fim (deque double-ended queue)

Filas de espera com prioridades (priority queues)

Implementao das filas:


usando vectores/arrays (circulares ou no) utilizando um apontador para ns de

Fila
Implementao em C usando arrays Vantagens:

Facilidade de implementao.

Desvantagens:
Vectores possuem um espao limitado para armazenamento de dados. Necessidade de definir um espao grande o suficiente para a fila Necessidade de um indicador para o inicio e para o fim da fila

Mtodo de Implementao
A adio de elementos fila resulta no incremento do indicador do fim da fila A remoo de elementos da fila resulta no incremento do indicador do inicio da fila

Implementao de Filas em C

Usamos o vector para armazenar os elementos da fila e duas variveis, inic e fim, para armazenar as posies dentro do vector do primeiro e ltimo elementos da fila:
#define MAXFILA 100 struct fila{
int elem[MAXFILA]; int inic, fim; }f;

evidente que usar vector para armazenar uma fila introduz a possibilidade de estouro, caso a fila fique maior que o tamanho do vector.

Capitulo III: rvores

Docente: Dikiefu Fabiano, Msc

Sumrio
3.1 Introduo 3.2 Tipos de rvores 3.3 rvores binrias 3.3.1 Estrutura de uma rvore binria 3.3.2 Descrio 3.3.3 Altura 3.3.4 Representao em C 3.4 rvores Genricas 3.4.1 Estrutura de uma rvore genrica 3.4.2 Representao em C 3.4.3 Problemas com Representao

Introduo

As estruturas anteriores so chamadas de unidimensionais (ou lineares)


Exemplo so listas sequenciais e ligadas

No podem ser usadas como hierarquias.


Exemplo: rvore de directrios

rvore uma estrutura adequada para representar hierarquias A forma mais natural para definirmos uma estrutura de rvore usando

Estrutura de rvores

Uma rvore composta por um conjunto de ns. Existe um n r, denominado raiz, que contm zero ou mais sub-rvores, cujas razes so ligadas a r. Esses ns razes das sub-rvores so ditos filhos do n pai, no caso r. Ns com filhos so chamados ns internos e ns que no tm filhos so chamados folhas, ou ns externos. tradicional desenhar as rvores com a raiz para cima e folhas para baixo, ao contrrio do que seria de se esperar.

Estrutura de rvores

Por adoptarmos essa forma de representao grfica, no representamos explicitamente a direco dos ponteiros, subentendendo que eles apontam sempre do pai para os filhos.

Tipos de rvores

O nmero de filhos permitido por n e as informaes armazenadas em cada n diferenciam os diversos tipos de rvores existentes. Existem dois tipos de rvores:

rvores binrias, onde cada n tem, no mximo, dois filhos. rvores genricas, onde o nmero de filhos indefinido.

Estruturas recursivas sero usadas como base para o estudo e a implementao das operaes com rvores.

rvore Binria
Um exemplo de utilizao de rvores binrias est na avaliao de expresses. Como trabalhamos com operadores que esperam um ou dois operandos, os ns da rvore para representar uma expresso tm no mximo dois filhos. Nessa rvore, os ns folhas representam operandos e os ns internos operadores.

rvores Binrias

Esta rvore representa a expresso: (3+6)*(4-1)+5

Estrutura de uma AB
Numa rvore binria, cada n tem zero, um ou dois filhos. De maneira recursiva, podemos definir uma rvore binria como sendo:

uma rvore vazia; ou um n raiz tendo duas sub-rvores, identificadas como a sub-rvore da direita (sad) e a sub-rvore da esquerda (sae).

Estrutura de uma AB
Os ns a, b, c, d, e, f formam uma rvore binria da seguinte maneira: A rvore composta do n a, da subrvore esquerda formada por b e d, e da subrvore direita formada por c, e e f. O n a representa a raiz da rvore e os ns b e c as razes das sub-rvores. Finalmente, os ns d, e e f so folhas da rvore.

Descrio de AB

Para descrever rvores binrias, podemos usar a seguinte notao textual:


A rvore vazia representada por <>, e rvores no vazias por <raiz sae sad>.

Com essa notao, a rvore da Figura anterior representada por:


<a<b<><d<><>>> <c<e<><>><f<><>>>>.

Descrio de AB
Uma sub-rvore de uma rvore binria sempre especificada como sendo a sae ou a sad de uma rvore maior, e qualquer das duas subrvores pode ser vazia. As duas rvores da Figura abaixo so distintas.

Altura de uma AB

Uma propriedade fundamental de todas as rvores que s existe um caminho da raiz para qualquer n. Podemos definir a altura de uma rvore como sendo o comprimento do caminho mais longo da raiz at uma das folhas. A altura de uma rvore com um nico n raiz zero e, por conseguinte, dizemos que a altura de uma rvore vazia negativa e vale -1.

Representao em C

Podemos definir um tipo para representar uma rvore binria. Vamos considerar que a informao a ser armazenada so valores de caracteres simples. Vamos inicialmente discutir como podemos representar uma estrutura de rvore binria em C. Que estrutura podemos usar para representar um n da rvore? Cada n deve armazenar trs informaes: a informao propriamente dita, no caso um caractere, e dois ponteiros para as subrvores, esquerda e direita.

Representao em C
struct arv { char info; struct arv* esq; struct arv* dir; }; typedef struct arv Arv; Da mesma forma que uma lista encadeada representada por um ponteiro para o primeiro n, a estrutura da rvore como um todo representada por um ponteiro para o n raiz.

Criando rvores
Arv* inicializa(void) { return NULL; } Arv* cria(char c, Arv* sae, Arv* sad) {
Arv* p=(Arv*)malloc(sizeof(Arv)); p->info = c; p->esq = sae; p->dir = sad; return p;

rvore Vazia
int vazia(Arv* a) { return a==NULL; }

Exemplo

Exemplo: Usando as operaes inicializa e cria, crie uma estrutura que represente a seguinte rvore.

Exemplo
/* sub-rvore com 'd' */ Arv* a1= cria('d',inicializa(),inicializa()); /* sub-rvore com 'b' */ Arv* a2= cria('b',inicializa(),a1); /* sub-rvore com 'e' */ Arv* a3= cria('e',inicializa(),inicializa()); /* sub-rvore com 'f' */ Arv* a4= cria('f',inicializa(),inicializa()); /* sub-rvore com 'c' */ Arv* a5= cria('c',a3,a4); /* rvore com raiz 'a'*/ Arv* a = cria('a',a2,a5 );

Exemplo

Alternativamente, a rvore poderia ser criada recursivamente com uma nica atribuio, seguindo a sua estrutura. Como isso pode ser feito?

Exemplo
Arv* a = cria('a', cria('b', inicializa(), cria('d', inicializa(), inicializa()) ), cria('c', cria('e', inicializa(), inicializa()), cria('f', inicializa(), inicializa()) ) );

Exibe Contedo da rvore


void imprime (Arv* a) { if (!vazia(a)){ printf("%c ", a->info); /* mostra raiz */ imprime(a->esq); /* mostra sae */ imprime(a->dir); /* mostra sad */ } } Como chamada essa forma de exibio? E para exibir na forma in-fixada? E na ps-fixada?

Liberando Memria
Arv* libera (Arv* a){ if (!vazia(a)){ libera(a->esq); /* libera sae */ libera(a->dir); /* libera sad */ free(a); /* libera raiz */ } return NULL; }

Criao e Liberao
Vale a pena notar que a definio de rvore, por ser recursiva, no faz distino entre rvores e sub-rvores. Assim, cria pode ser usada para acrescentar uma sub-rvore em um ramo de uma rvore, e libera pode ser usada para remover uma sub-rvore qualquer de uma rvore dada.

Criao e Liberao
Considerando a criao da rvore feita anteriormente, podemos acrescentar alguns ns, com: a->esq->esq = cria('x', cria('y',inicializa(),inicializa()), cria('z',inicializa(),inicializa()) ); E podemos liberar alguns outros, com: a->dir->esq = libera(a->dir->esq); Deixamos como exerccio a verificao do resultado final dessas operaes.

Buscando um Elemento

Essa funo tem como retorno um valor booleano (um ou zero) indicando a ocorrncia ou no do carcter na rvore.
int busca (Arv* a, char c){ if (vazia(a)) return 0; /* no encontrou */ else return a->info==c ||busca(a->esq,c) ||busca(a->dir,c); }

Buscando um Elemento
Podemos dizer que a expresso: return c==a->info || busca(a->esq,c) || busca(a->dir,c); equivalente a: if (c==a->info) return 1; else if (busca(a->esq,c)) return 1; else return busca(a->dir,c);

rvores Genricas
Como vimos, numa rvore binria o nmero de filhos dos ns limitado em no mximo dois. No caso da rvore genrica, esta restrio no existe. Cada n pode ter um nmero arbitrrio de filhos. Essa estrutura deve ser usada, por exemplo, para representar uma rvore de directrios. Supor que no h rvore vazia.

Exemplo

Representao em C
Devemos levar em considerao o nmero de filhos que cada n pode apresentar. Se soubermos que numa aplicao o nmero mximo de filhos 3, podemos montar uma estrutura com 3 campos apontadores, digamos, f1, f2 e f3. Os campos no utilizados podem ser preenchidos com o valor nulo NULL, sendo sempre utilizados os campos em ordem. Assim, se o n n tem 2 filhos, os campos f1 e f2 seriam utilizados, nessa ordem, ficando f3 vazio.

Representao em C
Prevendo um nmero mximo de filhos igual a 3, e considerando a implementao de rvores para armazenar valores de caracteres simples, a declarao do tipo que representa o n da rvore poderia ser:
struct arv3 { char val; struct arv3 *f1, *f2, *f3;

Exemplo

Problemas com Representao


Como se pode ver no exemplo, em cada um dos ns que tem menos de trs filhos, o espao correspondente aos filhos inexistentes desperdiado. Alm disso, se no existe um limite superior no nmero de filhos, esta tcnica pode no ser aplicvel. O mesmo acontece se existe um limite no nmero de ns, mas esse limite ser raramente alcanado, pois estaramos tendo um grande desperdcio de espao de memria com os campos no utilizados. H alguma soluo para contornar tal problema?

Soluo
Uma soluo que leva a um aproveitamento melhor do espao utiliza uma lista de filhos: um n aponta apenas para seu primeiro (prim) filho, e cada um de seus filhos, excepto o ltimo, aponta para o prximo (prox) irmo. A declarao de um n pode ser:
struct arvgen { char info; struct arvgen *prim; struct arvgen *prox; }; typedef struct arvgen ArvGen;

Exemplo da Soluo

Exemplo da Soluo
Uma das vantagens dessa representao que podemos percorrer os filhos de um n de forma sistemtica, de maneira anloga ao que fizemos para percorrer os ns de uma lista simples. Com o uso dessa representao, a generalizao da rvore apenas conceptual, pois, concretamente, a rvore foi transformada em uma rvore binria, com filhos esquerdos apontados por prim e direitos apontados por prox.

Criao de uma rvore Genrica


ArvGen* cria (char c) { ArvGen *a =(ArvGen*)malloc(sizeof(ArvGen)); a->info = c; a->prim = NULL; a->prox = NULL; return a; }

Insero
Como no vamos atribuir nenhum significado especial para a posio de um n filho, a operao de insero pode inserir a sub-rvore em qualquer posio. Vamos optar por inserir sempre no incio da lista que, como j vimos, a maneira mais simples de inserir um novo elemento numa lista ligada.

void insere (ArvGen* a, ArvGen* f) { f->prox = a->prim; a->prim = f; }

Exemplo Criao rvore Genrica

Com as funes cria e insere como construir a rvore abaixo?

Exemplo Criao rvoreGen


/* cria ns como folhas */ ArvGen* a = cria('a'); ArvGen* b = cria('b'); ArvGen* c = cria('c'); ArvGen* d = cria('d'); ArvGen* e = cria('e'); ArvGen* f = cria('f'); ArvGen* g = cria('g'); ArvGen* h = cria('h'); ArvGen* i = cria('i'); ArvGen* j = cria('j'); ... /* monta a hierarquia */ insere(c,d); insere(b,e); insere(b,c); insere(i,j); insere(g,i); insere(g,h); insere(a,g); insere(a,f); insere(a,b);

Impresso (pr-ordem)
void imprime (ArvGen* a) { ArvGen* p; printf("%c\n",a->info); for (p=a->prim; p!=NULL; p=p->prox) imprime(p); }

Busca de Elemento
int busca (ArvGen* a, char c) { ArvGen* p; if (a->info==c) return 1; else { for (p=a->prim; p!=NULL; p=p->prox) { if (busca(p,c)) return 1; } } return 0; }

Liberao de Memria
O nico cuidado que precisamos tomar na programao dessa funo a de liberar as subrvores antes de liberar o espao associado a um n (isto , usar ps-ordem).

Liberao de Memria
void libera (ArvGen* a){ ArvGen* p = a->prim; while (p!=NULL) { ArvGen* t = p->prox; libera(p); p = t; } free(a); }

Capitulo IV - Ordenao

Docente: Dikiefu Fabiano, Msc

Sumrio
4. Ordenao Interna 4.1 Ordenao por Seleco 4.2 Ordenao por Insero 4.3 Shellsort 4.4 Quicksort 4.5 Heapsort

102

Conceitos Bsicos
Ordenar: processo de rearranjar um conjunto de objectos em uma ordem ascendente ou descendente. A ordenao visa facilitar a recuperao posterior de itens do conjunto ordenado. A maioria dos mtodos de ordenao baseada em comparaes das chaves. Existem mtodos de ordenao que utilizam o princpio da distribuio.

103

Conceitos bsicos
Um mtodo de ordenao estvel se a ordem relativa dos itens com chaves iguais no se altera durante a ordenao. Alguns dos mtodos de ordenao mais eficientes no so estveis. A estabilidade pode ser forada quando o mtodo no-estvel. Sedgewick (1988) sugere agregar um pequeno ndice a cada chave antes de ordenar, ou ento aumentar a chave de alguma outra forma.

104

Classificao dos mtodos de ordenao


1. Ordenao interna: arquivo a ser ordenado cabe todo na memria principal. 2. Ordenao externa: arquivo a ser ordenado no cabe na memria principal. Diferenas entre os mtodos: Em um mtodo de ordenao interna, qualquer registo pode ser imediatamente cessado. Em um mtodo de ordenao externa, os registos so cessados sequencialmente ou em grandes blocos.

105

Classificao dos mtodos de ordenao interna


Mtodos simples: Adequados para pequenos arquivos. Produzem programas pequenos. Mtodos eficientes: Adequados para arquivos maiores. Usam menos comparaes. As comparaes so mais complexas nos detalhes. Mtodos simples so mais eficientes para pequenos arquivos.

106

Ordenao por Seleco


Um dos algoritmos mais simples de ordenao. Algoritmo: Seleccione o menor item do vector. Troque-o com o item da primeira posio do vector. Repita essas duas operaes com os n 1 itens restantes, depois com os n 2 itens, at que reste apenas um elemento.

As chaves em negrito sofreram uma troca entre si.


107

Ordenao por Seleco


Vantagens: Custo linear no tamanho da entrada para o nmero de movimentos de registos. o algoritmo a ser utilizado para arquivos com registos muito grandes. muito interessante para arquivos pequenos.
Desvantagens: O facto de o arquivo j estar ordenado no ajuda em nada, pois o custo continua quadrtico. O algoritmo no estvel.

108

Ordenao por Seleco


void seleccaot(int vector[],int tam) { int i, j, min, aux; for(i=0; i<tam-1; i++) { min = i; aux = vector[i]; for(j=i+1; j<tam; j++) { if (vector[j] < aux) { min=j; aux=vector[j]; } } aux = vector[i]; vector[i] = vector[min]; vector[min] = aux; } }

109

Cdigo da ordenao por seleccao com strings

void seleccao_string() { int j; for(i=0; i<n-1; i++) { for(j=i+1; j<n; j++) { if(strcmpi(vector[i], vector[j])>0) { strcpy(aux_char, vector[i]); strcpy(vector[i], vector[j]); strcpy(vector[j], aux_char); } } } }

Bubble Sort

O bubble sort, ou ordenao por flutuao (literalmente "por bolha"), um algoritmo de ordenao dos mais simples. A ideia percorrer o vector diversas vezes, a cada passagem fazendo flutuar para o topo o maior elemento da sequncia. Essa movimentao lembra a forma como as bolhas em um tanque de gua procuram seu prprio nvel, e disso vem o nome do algoritmo

Exemplo: bolha1
void bolha( int v[], int qtd ) { int i; int j, aux, k = qtd - 1 ; for(i = 0; i < qtd; i++) { for(j = 0; j < k; j++) { if(v[j] > v[j+1]) { aux = v[j]; v[j] = v[j+1]; v[j+1]=aux; } } } }

Exemplo: bolha2
void trocabolha( int v[], int i ) { aux = v[i]; v[i] = v[i+1]; v[i+1]=aux; } void bolha( int v[], int qtd ) { int i,j; for( j = 0; j < qtd; j++ ) { for( i = 0; i < qtd - 1; i++ ) { if( v[i] > v[i+1] ) { trocabolha( v, i ); } } } }

Mtodo de ordenao Bolha com ordenao de strings.


void bolha_string(int v[], int qtd){ int i, trocou; char aux; do { qtd--; trocou = 0; for(i = 0; i < qtd; i++) if(strcasecmp(v[i],v[i + 1])>0) { strcpy(aux, v[i]); strcpy(v[i], v[i + 1]); strcpy(v[i + 1], aux); trocou = 1; } }while(trocou==1); }

Ordenao por Insero


Mtodo preferido dos jogadores de cartas. Algoritmo: Em cada passo a partir de i =2 faa: Seleccione o i-simo item da sequncia fonte. Coloque-o no lugar apropriado na sequncia destino de acordo com o critrio de ordenao.

As chaves em negrito representam a sequncia destino.


115

Ordenao por Insero


void insertionSort(int vector[], int tam) { int i, j; int key; for (j = 1; j < tam; ++j) { key = vector[j]; i = j - 1; while (vector[i] > key && i >= 0) { vector[i+1] = vector[i]; --i; } vector[i+1] = key; } }

A colocao do item no seu lugar apropriado na sequncia destino realizada movendo-se itens com chaves maiores para a direita e ento inserindo o item na posio deixada vazia.
116

Ordenao por Insero


Consideraes sobre o algoritmo: O processo de ordenao pode ser terminado pelas condies: Um item com chave menor que o item em considerao encontrado. O final da sequncia destino atingido esquerda. Soluo: Utilizar um registo sentinela na posio zero do vector.

117

Ordenao por Insero


O nmero mnimo de comparaes e movimentos ocorre quando os itens esto originalmente em ordem. O nmero mximo ocorre quando os itens esto originalmente na ordem reversa. o mtodo a ser utilizado quando o arquivo est quase ordenado. um bom mtodo quando se deseja adicionar uns poucos itens a um arquivo ordenado, pois o custo linear. O algoritmo de ordenao por insero estvel.

118

Shellsort
Proposto por Shell em 1959. uma extenso do algoritmo de ordenao por insero. Problema com o algoritmo de ordenao por insero: Troca itens adjacentes para determinar o ponto de insero. So efectuadas n 1 comparaes e movimentaes quando o menor item est na posio mais direita no vector. O mtodo de Shell contorna este problema permitindo trocas de registos distantes um do outro.
119

Shellsort
Os itens separados de h posies so rearranjados. Todo h-simo item leva a uma sequncia ordenada. Tal sequncia dita estar h-ordenada. Exemplo de utilizao:

Quando h = 1 Shellsort corresponde ao algoritmo de

120

Shellsort
Como escolher o valor de h: Sequncia para h: h(s) = 3h(s 1) + 1, para s > 1 h(s) = 1, para s = 1. Knuth (1973, p. 95) mostrou experimentalmente que esta sequncia difcil de ser batida por mais de 20% em eficincia. A sequncia para h corresponde a 1, 4, 13, 40, 121, 364, 1.093, 3.280, . . .

121

Shellsort
void shellsort ( Int v [ ] , int n) { int i,j,x,h = 1; do h = h *3 + 1; while (h < n) ; do { h /= 3; for ( i = h + 1; i <= n; i ++) { x = v[ i ] ; j = i ; while (v[ j h] > x > 0) { v[ j ] = v[ j h] ; j = h; i f ( j <= h) break; } v[ j ] = x; } } while (h != 1) ; }

A implementao do Shellsort no utiliza registos sentinelas. Seriam necessrios h registos sentinelas, uma para cada h-ordenao.

122

Shellsort
Vantagens: Shellsort uma ptima opo para arquivos de tamanho moderado. Sua implementao simples e requer uma quantidade de cdigo pequena. Desvantagens: O tempo de execuo do algoritmo sensvel ordem inicial do arquivo. O mtodo no estvel,

123

Quicksort
Proposto por Hoare em 1960 e publicado em 1962. o algoritmo de ordenao interna mais rpido que se conhece para uma ampla variedade de situaes. Provavelmente o mais utilizado. A ideia bsica dividir o problema de ordenar um conjunto com n itens em dois problemas menores. Os problemas menores so ordenados independentemente. Os resultados so combinados para produzir a soluo final.
124

Quicksort
A parte mais delicada do mtodo relativa ao mtodo partio. O vector v[esq..dir ] rearranjado por meio da escolha arbitrria de um piv x. O vector v particionado em duas partes: A parte esquerda com chaves menores ou iguais a x. A parte direita com chaves maiores ou iguais a x.

125

Quicksort
Algoritmo para o particionamento: 1. Escolha arbitrariamente um piv x. 2. Percorra o vector a partir da esquerda at que v[i] x. 3. Percorra o vector a partir da direita at que v[j] x. 4. Troque v[i] com v[j]. 5. Continue este processo at os apontadores i e j se cruzarem. Ao final, o vector v[esq..dir ] est particionado de tal forma que: Os itens em v[esq], v[esq + 1], . . . , v[j] so menores ou iguais a x. Os itens em v[i], v[i + 1], . . . , v[dir ] so maiores ou iguais a x.

126

Quicksort
Ilustrao do processo de partio:

O piv x escolhido como sendo v[(i+j) / 2]. Como inicialmente i = 1 e j = 6, ento x = v[3] = D. Ao final do processo de partio i e j se cruzam em i = 3e j = 2.
127

Quicksort
void swap(int* a, int* b) { int tmp; tmp = *a; *a = *b; *b = tmp; } int partition(int vec[], int left, int right) { int i, j; i = left; for (j = left + 1; j <= right; ++j) { if (vec[j] < vec[left]) { ++i; swap(&vec[i], &vec[j]); } } swap(&vec[left], &vec[i]); return i; }
128

Quicksort
Mtodo ordena e algoritmo Quicksort : void quickSort(int vec[], int left, int right) { int r; if (right > left) { r = partition(vec, left, right); quickSort(vec, left, r - 1); quickSort(vec, r + 1, right); } }

129

Implementao usando 'fat pivot': void sort(int array[], int begin, int end) { int pivot = array[begin]; int i = begin + 1, j = end, k = end, t; while (i < j) { if (array[i] < pivot) i++; else if (array[i] > pivot) { j--; k--; t = array[i]; array[i] = array[j]; array[j] = array[k]; array[k] = t;

} else {
j--; swap(array[i], array[j]); } } i--; swap(array[begin], array[i]); if (i - begin > 1) sort(array, begin, i); if (end - k > 1) sort(array, k, end); }

Lembrando que quando voc for chamar a funo recursiva ter que chamar a mesma da seguinte forma ordenar_quicksort(0,n-1). O 0(zero) serve para o incio receber a posio zero do vector e o fim ser o tamanho do vector -1.

void ordenar_quicksort(int ini, int fim) { int i = ini, f = fim; char pivo[50]; strcpy(pivo,vector[(ini+fim)/2]); if (i<=f) { while (strcmpi(vector[i],pivo)<0) i++; while (strcmpi(vetor[f],pivo)>0) f--; if (i<=f) { strcpy (aux_char,vetor[i]); strcpy (vetor[i],vetor[f]); strcpy (vetor[f],aux_char); i++; f--; } }

if (f>ini) ordenar_quicksort_nome(ini,f); if (i<fim) ordenar_quicksort_nome(i,fim);


}

Quicksort
Exemplo do estado do vector em cada chamada recursiva do procedimento Ordena:

O piv mostrado em negrito.


135

Quicksort
Vantagens: extremamente eficiente para ordenar arquivos de dados. Necessita de apenas uma pequena pilha como memria auxiliar. Desvantagens: Sua implementao muito delicada e difcil: Um pequeno engano pode levar a efeitos inesperados para algumas entradas de dados. O mtodo no estvel.

136

Heapsort
Possui o mesmo princpio de funcionamento da ordenao por seleco. Algoritmo: 1. Seleccione o menor item do vector. 2. Troque-o com o item da primeira posio do vector. 3. Repita estas operaes com os n 1 itens restantes, depois com os n 2 itens, e assim sucessivamente. O custo para encontrar o menor (ou o maior) item entre n itens n 1 comparaes. Isso pode ser reduzido utilizando uma fila de prioridades.
137

Heaps
uma sequncia de itens com chaves c[1], c[2], . . . , c[n], tal que: c[i] c[2i], c[i] c[2i + 1], para todo i = 1, 2, . . . , n/2. A definio pode ser facilmente visualizada em uma rvore binria completa: rvore binria completa: Os ns so numerados de 1 a n. O primeiro n chamado raiz. O n k/2 o pai do n k, para 1 < k n. Os ns 2k e 2k + 1 so os filhos esquerda e direita do n k, para 1 k k/2
138

Heaps
As chaves na rvore satisfazem a condio do heap. As chaves em cada n so maiores do que as chaves em seus filhos. A chave no n raiz a maior chave do conjunto. Uma rvore binria completa pode ser representada por um arranjo:

A representao extremamente compacta. Permite caminhar pelos ns da rvore facilmente. Os filhos de um n i esto nas posies 2i e 2i + 1. O pai de um n i est na posio i/2.

139

Heaps
Na representao do heap em um arranjo, a maior chave est sempre na posio 1 do vector. Os algoritmos para implementar as operaes sobre o heap operam ao longo de um dos percursos da rvore. Um algoritmo elegante para construir o heap foi proposto por Floyd em 1964. O algoritmo no necessita de nenhuma memria auxiliar. Dado um vector v[1], v[2], . . . , v[n]. Os itens v[n/2 + 1], v[n/2 + 2], . . . , v[n] formam um heap: Neste intervalo no existem dois ndices i e j tais que j = 2i ou j = 2i + 1.

140

Heapsort
void heapsort(tipo a[], int n) { int i = n/2, pai, filho; tipo t; for (;;) { if (i > 0) { i--; t = a[i]; } else { n--; if (n == 0) return; t = a[n]; a[n] = a[0]; }
141

pai = i; filho = i*2 + 1; while (filho < n) { if ((filho + 1 < n) && (a[filho + 1] > a[filho])) filho++; if (a[filho] > t) { a[pai] = a[filho]; pai = filho; filho = pai*2 + 1; } else break; } a[pai] = t; } }

142

Heapsort
Algoritmo:

Os itens de v[4] a v[7] formam um heap. O heap estendido para a esquerda (esq = 3), englobando o item v[3], pai dos itens v[6] e v[7]. A condio de heap violada: O heap refeito trocando os itens D e S. O item R incluindo no heap (esq = 2), o que no viola a condio de heap. O item O incluindo no heap (esq = 1). A Condio de heap violada: O heap refeito trocando os itens O e S, encerrando o processo.
143

Heapsort
Exemplo da operao de aumentar o valor da chave do item na posio i:

O tempo de execuo do procedimento AumentaChave em um item do heap O(log n).


144

Construo de Heap
Algoritmo:

1. Construir o heap. 2. Troque o item na posio 1 do vector (raiz do heap) com o item da posio n. 3. Use o procedimento Refaz para reconstituir o heap para os itens v[1], v[2], . . . , v[n 1]. 4. Repita os passos 2 e 3 com os n 1 itens restantes, depois com os n 2, at que reste apenas um item.

Heapsort
Exemplo de aplicao do Heapsort :

1 S R O N E D A

2 R N N E D A D

3 4 5 O E N O E D A E D A D O A N E

6 7 A D A S R

O caminho seguido pelo procedimento Refaz para reconstituir a condio do heap est em negrito. Por exemplo, aps a troca dos itens S e D na segunda linha da Figura, o item D volta para a posio 5, aps passar pelas posies 1 e 2.

Referncias
1. Levitin, Anany Introduction to the design and analysis of algorithm Addison-Wesley 2003 2. Pedro Neto, Joo Programao, algoritmo e estrutura de dados escola editora 2008 3. Damas, Lus Programao em C 4. Ziviani, Nivio e Botelho, Fabiano Cupertino, Projecto de Algoritmos com implementao em Pascal e C editora Thomson, 2007 5. http://www.icmc.usp.br/~sce182/arvore.html 6. http://w3.ualg.pt/~hshah/ped 7. http://www.liv.ic.unicamp.br/~bergo/mc202/

You might also like