Professional Documents
Culture Documents
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
Ordenao
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
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)
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
int x =5; printf(%d\n, x); // imprime 5 func(&x); // imprime 6 printf(%d\n, x); // imprime 6 }
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
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(); }
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.
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
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
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
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
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);
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
Um tipo n, representa cada elemento da lista. e um tipo lista que contm um ponteiro para n cabea.
} NO
typedef struct lista{ NO *head; }LISTA
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; }
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 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
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.
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).
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
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:
Inicializar ou limpar:
inicializa Colocar a fila num estado pronto a ser utilizada
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
Tipos de filas:
Filas de espera (queues)
com duplo fim (deque double-ended queue)
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.
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
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
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
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()) ) );
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
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.
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.
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
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
105
106
108
109
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 ); } } } }
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
117
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:
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--; } }
Quicksort
Exemplo do estado do vector em cada chamada recursiva do procedimento Ordena:
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:
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/