Ed1

1,754 views
1,620 views

Published on

0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
1,754
On SlideShare
0
From Embeds
0
Number of Embeds
3
Actions
Shares
0
Downloads
167
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Ed1

  1. 1. Estrutura de Dados I Docente: Dikiefu Fabiano,Msc
  2. 2. Sumário1. Introdução 1.1 Tipo de dados Abstracto -TDA 1.2 Ponteiros 1.3 Funções 1.4 Estruturas e tipos de dados definidos pelo programador2. Listas Lineares 2. 1 Listas Sequenciais 2.2 Listas ligadas 2.3 Listas Circulares 2.4 Listas duplamente ligadas 2.5 Pilhas 2.5.1Implementação sequencial da pilha 2.5.2Implementação dinâmica da pilha 2.6 Filas 2.6.1Implementação sequencial da fila 2.6.2Implementação dinâmica da fila
  3. 3. 3. Árvores 3.1 Introdução 3.2 Representação de árvores 3.2 Definição 3.3 Terminologia 3.4 Árvores binárias 3.5 Árvores balanceadas 3.6 Definição da estrutura de dados4. Ordenação 4.1 Ordenação por Selecção 4.2 Ordenação por Inserção 4.3 Shellsort 4.4 Quicksort 4.5 Heapsort5. Pesquisa 5.1 Pesquisa Sequencial 5.2 Pesquisa Binária 5.3 Árvores de Pesquisa
  4. 4. Capitulo I: IntroduçãoSumário: 1.1Tipo de Dado Abstracto (TDA) 1.2Ponteiros 1.3Funções 1.4Estruturas 1.5 Tipos de dados definidos pelo programador
  5. 5. 1.1 Tipo de Dados Abstracto -TDA Tipos de Dados ◦ Tipo de dados de uma variáveis  define o conjunto de valores que a variável pode assumir.  Especifica a quantidade de bytes que deve ser reservadas a ela. ◦ Tipos de dados podem ser vistos como métodos para interpretar o conteúdo da memória do computador ◦ Podemos ver o conceito de tipo de dados de uma outra perspectiva: não em termos do que um computador pode fazer (interpretar os bits …) mas em termos do que o utilizador desejam fazer (somar dois inteiros…)
  6. 6. 1.1 Tipo de Dado Abstracto - TDA Tipo de Dado Abstracto ◦ Agrupa a estrutura de dados juntamente com as operações que podem ser feitas sobre esses dados ◦ O TDA encapsula a estrutura de dados. Os utilizadores do TDA só tem acesso a algumas operações disponibilizadas sobre esses dados ◦ Utilizador do TDA x Programador do TDA  Utilizador só “enxerga” a interface, não a sua implementação  Dessa forma, o utilizador pode abstrair da implementação específica.  Qualquer modificação nessa implementação fica restrita ao TDA ◦ A escolha de uma representação específica é fortemente influenciada pelas operações a serem executadas
  7. 7. 1.1 Tipo de Dado Abstracto -TDA Estrutura de Dados - ED ◦ É um método particular de se implementar um TDA ◦ Cada ED é construída dos tipos primitivos (inteiro, real, char, …) ou dos tipos compostos (array, registo, …) de uma linguagem de programação. ◦ Algumas operações realizadas em TDA são: criação, inclusão, busca e remoção de dados. Exemplos de TDA ◦ Lineares: Não Lineares:  Listas ligadas . Árvores  Pilhas . Grafos  Filas
  8. 8. TDA-Exemplo Existem vários métodos para especificar um TDA. O método que usaremos é semi-formal e faz uso intensivo da notação de C. Para ilustrar o conceito de um TDA e método de especificação, examinemos o TDA RACIONAL que corresponde ao conceito matemático de um número racional. Um número racional é o que pode ser expresso como o quociente de dois inteiros. As operações sobre números racionais que definimos são: ◦ Criação de um número racional ◦ Adição ◦ Multiplicação ◦ Teste de igualdade.
  9. 9. TDA-Exemplo//definição de valorabstract typedef <integer, integer> RACIONAL;condiction RACIONAL [1] <> 0;//definição de operadorabstract RACIONAL criaracional(a, b)int a, b;precondition b <> 0;postcondition criaracional[0]== a; criaracional[1]== b;abstract RACIONAL soma (a, b)RACIONAL a, b;postcondition soma[1]== a[1]*b[1]; soma[0]==a[0]*b[1]+b[0]*a[1];
  10. 10. abstract RACIONAL mult(a, b)RACIONAL a, b;postcondition mult[0]==a[0]*b[0]; mult[1]==a[1]*b[1];abstract RACIONAL igual(a,b)RACIONAL a, b;postcondition igual==(a[0]*b[1]==a[1]*b[0]);Exercício:Crie o TODA para o conjunto de números complexos com todasas suas operações básicas.
  11. 11. 1.2 Apontadores Na realidade, C permite que o programador referencie a posição de objectos bem como os próprios objectos (isto é, o conteúdo de suas posições)
  12. 12. 1.2 Apontadores• O objectivo: – Armazenar o endereço de outra variável.• Sintaxe tipo * ptr • ptr - é o nome da variável do tipo apontador • tipo – o tipo da variável para qual apontará. • * - indica que é uma variável do tipo apontador• Exemplo 1: char *pc; int *pi; float *pf
  13. 13. 1.2 Apontadores Um ponteiro é como qualquer outro tipo de dado em C. O valor de um ponteiro é uma posição de memória da mesma forma que o valor de um inteiro é um número. Os valores dos ponteiros podem ser atribuídos como qualquer outro valor. A conversão de pf do tipo “ponteiro para um número 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, então pi +1 é o ponteiro para o inteiro imediatamente seguinte ao inteiro *pi em memória, pi–1 é o ponteiro para o inteiro imediatamente anterior a *pi.
  14. 14. Apontadores Por exemplo, suponha que determinada máquina usa endereçamento de bytes, que um inteiro exija 4 bytes e que valor de pi seja 100 (isto é, pi aponta para inteiro *pi na posição 100). Sendo assim, o valor de pi–1 é 96, o valor de pi+1 é 104. De modo semelhante, se o valor da variável pc é 100 e um caractere tem um byte, pc – 1 refere-se a posição 99 e pc+1 à posição 101.
  15. 15. Apontadores Uma área na qual os ponteiros x =5 de C desempenham um printf(“%dn”, x) // imprime 5 notável papel é passagem de parâmetro para funções. func(x); // imprime 6 Normalmente, os parâmetros printf(“%dn”, x) // imprime 5 são passados para uma … função em C por valor, isto é, func(y) os valores sendo passados são copiados nos parâmetros int y da função de chamada no { momento em que a função for ++y; chamada. Se o valor de um printf(“%dn”, y); parâmetro for alterado dentro da função, o valor no } programa chamada não será modificado.
  16. 16. Se precisar usar func para modificar o valor de x, precisaremos passaro endereço de xx =5printf(“%dn”, x) // imprime 5func(&x); // imprime 6printf(“%dn”, x) //imprime 6…func(py)int *py{ ++(*py); printf(“%dn”, *py);}
  17. 17. 1.2.1 Inicialização automática deapontadores• Um bom hábito para evitar problemas de programação é a inicialização sempre dos apontadores• A constante simbólica NULL, quando colocada num apontador, indica que este não aponta para nenhuma variável. int x = 5; float pi = 3.1415; int px = &x; float * ppi = &pi;
  18. 18. 1.3 Funções Conceito: ◦ É uma unidade autónoma de código do programa e é desenhada para cumprir uma tarefa particular. Sintaxe tipo nome_da_função (parametros){ comandos; }
  19. 19. 1.3 Funções• Quando uma função não retorna um valor para função 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(); }
  20. 20. 1.3 Funções1.3.3 Função com retornoO tipo de retorno tem que serdeclarada. main(){ int b, e; Ex1 : printf(“Introduza a base e int potencia (int base, int expoente”); expoente){ scanf(“%d%d”, &b, &e); int i, pot=1; printf(“valor=%dn”, If (expoente<0) potencia(b,e)); getchar(); return; } for (i=1; i <= expoente; i++) pot=pot*base; Return pot; }
  21. 21. Estrutura de dados e tipo de dados definido peloutilizador Estruturas são peças contíguas de armazenamento que contém vários tipos simples agrupados numa entidade. Estrutura são manifestadas através da palavra reservada struct seguida pelo nome da estrutura e por uma área delimitada por colchetes que contém campos. struct pessoa { char nome[30]; int idade; char sexo; }; Para criação de novos tipos de estrutura de dados utiliza-se a palavra-chave: typedef typedef struct pessoa { char nome[30]; int idade; char sexo; } PESSOA;
  22. 22. 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> Ex … PESSOA func, *estud, &docente = func ; func.nome=“Adriano”; //atribui o valor Adriano para o campo nome func.idade=25; //atribui o valor 25 para o campo idade func.sexo=“M”; //atribui o valor M para o campo sexo estud=&func; //faz estud apontar para func estud->nome=“Joana”; //muda o valor do campo nome para Joana estud ->idade =22; //muda o valor do campo idade para 22 estud ->sexo =“F”; //muda o valor do campo sexo para F docente.Nome=“Pedro”; //Referências usam a notação de ponto …
  23. 23. 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; }
  24. 24. Capitulo II: ListasSumário2. Listas Lineares 2. 1 Listas Sequenciais 2.2 Listas ligadas 2.3 Listas Circulares 2.4 Listas duplamente ligadas 2.5 Pilhas 2.5.1 Implementação sequencial da pilha 2.5.2 Implementação dinâmica da pilha 2.6 Filas 2.6.1 Implementação sequencial da fila 2.6.2 Implementação dinâmica da fila 2.6.3 Fila circular
  25. 25. Listas Listas são uma colecção de objectos, onde os itens podem ser adicionados em qualquer posição arbitrária.
  26. 26. ListasPropriedades • 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
  27. 27. Listas - Formas deRepresentação Sequencial ◦ Explora a sequencialidade da memória do computador, de tal forma que os nós de uma lista sejam armazenados em endereço sequencias. Podem ser representado por um vector na memória principal ou um arquivo sequencial em disco. L Adão Adelin Basto Daniel Josefin … 0 1a 2 s 3 a 4 5 MAX-1 Ligada ◦ Esta estrutura é tida como uma sequencia de elementos ligados por ponteiros, ou seja, cada elemento deve conter, além do dado propriamente dito, uma referencia para o próximo elemento da lista. Adão Adelina Bastos Daniel Josefina L
  28. 28. Listas sequenciais Uma lista representada de forma sequencial é um conjunto de registos onde estão estabelecidas regras de precedência entre seus elementos. Implementação de operações pode ser feita utilizando array e registo, associando o elemento a[i] com o índice i. Características ◦ Os elementos na lista estão armazenados fisicamente em posições consecutivas ◦ A inserção de um elemento na posição a[i] causa o deslocamento a direita do elemento a[i] ao último. ◦ A eliminação do elemento a[i] requer o deslocamento à esquerda do a[i+1] ao último.
  29. 29. 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 máximo pré-estimado Quando usar ◦ Listas pequenas ◦ Inserção e remoção no fim da lista ◦ Tamanho máximo bem definido
  30. 30. Listas sequenciaisOperações básicas Definição #define MAX 50 //tamanho máximo da lista typedef char elem[20]; // tipo base dos elementos da lista typedef struct tlista{ elem v[MAX];//vector que contém a lista int n; //posição do último elemento da lista }; Tlista Adão Alberto Ana Daniela Carmen … n=5 v 0 1 2 3 4 5 6 MAX – 11.Criar uma lista vazia 2. Verificar se uma lista está vazia void criar(tlista *L) { int vazia(tlista L) { L -> n = 0; return (L .n == 0); } }
  31. 31. Listas sequenciaisOperações básicas3. Verificar se uma lista está cheia 4. Obter o tamanho de uma lista int cheia( tlista L) { int tamanho ( tlista L){ return (L .n == MAX); return (L.n); } }5. Obter o i-ésimo elemento de uma 6. Pesquisar um dado elemento,lista retornando a sua posição.int elemento( tlista L, int pos, elem *dado ) int posicao( tlista L, elem dado ){ { if ((pos > L.n) || (pos <=0)) int i; return (0); for (i=1; i<L.n; i++) *dado = L.v [pos - 1]; if(L.v[i-1] == dado) return 1; return i;} return 0; }
  32. 32. Listas SequenciaisOperações básicas7. Inserção de um elemento em uma 8. Remoção do elemento de umadeterminada posição determinada posição(requer o deslocamento à direita dos (requer o deslocamento à esquerda doselementos v(i+1) … v(n)) elementos v(p+1) … v(n))// retorna 0 se a posição for inválida ou se a /* o parâmetro dado irá receber o elementolista encontrado.// estiver cheia, caso contrário retorna 1 Retorna 0 se a posição for inválida , caso contrário retorna 1 */int inserir( tlista *L, int pos, elem dado ){ int i; int remover( tlista *L, int pos, elem *dado ) if (( L->n == MAX) || ( pos > L->n + 1 )) { return (0); int i; for (i = L->n; i >= pos; i--) if ( ( pos > L->n) || (pos <= 0) ) L->v[i] = L->v [i-1]; return (0); L->v[pos-1] = dado; *dado = L-> v[pos-1]; (L->n)++; for (i = pos; i <= (L->n) - 1; i++) return (1); L->v[i-1] = L->v [i];} (L->n)--; return (1); }
  33. 33. Listas Ligadas Uma lista ligada ou lista encadeada é uma estrutura de dados linear e dinâmica. Ela é composta por células que apontam para o próximo elemento da lista. Para "ter" uma lista ligada/encadeada, basta guardar seu primeiro elemento, e seu último elemento aponta para uma célula nula. Vantagens ◦ A inserção ou a retirada de um elemento na lista não implica na mudança de lugar de outros elementos; ◦ Não é necessário saber, anteriormente, o número máximo de elementos que uma lista pode ter, o que implica em não desperdiçar espaço na Memória Principal (Memória RAM) do computador. Desvantagens ◦ manipulação torna-se mais "perigosa" uma vez que, se o encadeamento (ligação) entre elementos da lista for mal feito, toda a lista pode ser perdida; ◦ Para acessar o elemento na posição n da lista, deve-se percorrer os n - 1 anteriores.
  34. 34. Listas ligadas As listas ligadas são conjunto de elementos ligados, onde cada elemento contem uma ligação com um ou mais elementos da lista. Uma lista ligada tem necessariamente uma variável ponteiro apontando para o seu primeiro elemento. Essa variável será utilizada sempre, mesmo que esteja vazia, e deverá apontar sempre para o início da lista. Cada elemento da lista ligada será composta por duas/três partes principais: uma parte conterá informações e a(s)
  35. 35. 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.
  36. 36. Listas Ligadas Uma lista ligada é composta por elementos alocadas em memória. Utiliza-se duas funções para alocar (desalocar) memória dinamicamente: ◦ malloc() ◦ free() Ex: L = (lista) malloc(sizeof(lista)); free(L);
  37. 37. Lista Simplesmente Ligada Cada elemento da lista possui uma única conexão. Assim, cada elemento será formado por um bloco de dados e um ponteiro para próximo elemento. Chamaremos cada elemento nó. Um nó é um conjunto de informações de tipos diferentes, portanto ela é uma estrutura definida pelo programador: struct no{ int dado; struct no *prox; //ponteiro para o próximo nó }
  38. 38. Listas Simplesmente Ligadas Sendo que uma memória alocada dinamicamente não possui o nome como no caso das variáveis declaradas no programa, toda lista deverá ter uma variável que apontará para o seu primeiro elemento. struct no *head; //cabeça da lista head=null; //inicia a lista com nulo
  39. 39. Listas Simplesmente Ligadas Podemos nos referenciar a uma lista ligada através do seu primeiro nó. Entretanto, toda vez que realizarmos uma inserção no início 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ó cabeça, e um ponteiro para o primeiro elemento da lista.
  40. 40. Listas Simplesmente Ligadas A implementação em C de uma lista simplesmente ligada com nó cabeça. Precisamos de dois tipos de dados: ◦ Um tipo nó, representa cada elemento da lista. ◦ e um tipo lista que contém um ponteiro para nó cabeça.
  41. 41. Listas Simplesmente Ligadas typedef struct no{ int dado; // o dado poderia ser de qualquer tipo struct no *prox; // ponteiro para próximo elemento } NO typedef struct lista{ NO *head; }LISTA
  42. 42. Listas Simplesmente Ligadas  As operações básicas sobre a estrutura são: criação da lista e de nó.1. Criação de lista LISTA *criaLista(){ LISTA *L; L=(LISTA *)malloc(sizeof(LISTA)); L->head = NULL; return L; }2. Criação de nó NO *criaNo(int dado){ NO *no; no = (NO *)malloc(sizeof(NO)); no->dado = dado; no->prox = NULL; return no; }
  43. 43. Listas Simplesmente Ligadas Ao contrário dos vectores, onde acessamos qualquer elemento usando um índice numérico, com uma lista ligada temos apenas acesso sequencial, por exemplo, para chegar ao decimo elemento temos que passar por todos nove elementos anteriores. Para percorrer uma lista, precisamos obter seu primeiro elemento e então obtemos os elementos subsequentes seguindo os ponteiros de próximo elemento em cada nó. NO *primeiro(LISTA *L){ return (L->head); } NO *proximo(NO *no){ return (no->prox); }
  44. 44. Listas Simplesmente Ligadas Andar para trás em uma lista simplesmente ligada não é tão simples, requer que a lista seja percorrida desde o início verificado se, para cada elemento x, proximo(x) é o elemento do qual procuramos o elemento anterior. NO *anterior(LISTA *L, NO *no){ NO *p; if(no==L->head) return NULL; p = primeiro(L); while(proximo(p)!=NULL){ if(proximo(p) == no) return p; p = proximo(p); } return NULL; }
  45. 45. Listas Simplesmente Ligadas Obter o último elemento também requer que toda a lista seja percorrida. NO *ultimo(LISTA *L){ NO *p; p = primeiro(L); if (p == NULL) return NULL; while(proximo(p)!=NULL) p = proximo(p); return p; }
  46. 46. Listas Simplesmente Ligadas Podemos querer 3 tipos de inserções em uma lista: ◦ no início, ◦ no fim, ◦ ou em uma posição arbitrária. Na lista simplesmente ligada a inserção no início é trivial: Fazemos o próximo do novo elemento apontar para o primeiro elemento da lista e fazemos com que o novo elemento seja o primeiro elemento (cabeça) da lista. A inserção em posição arbitrária 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 inserção no final da lista exige que encontremos seu último elemento.
  47. 47. Listas Simplesmente Ligadas Remoção de um elemento arbitrário 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); }
  48. 48. Listas duplamente ligadas Listas duplamente ligadas são usadas frequentemente nos problema que exija uma movimentação eficiente desde um nó quer para o seu antecessor, quer para o seu sucessor.frent NULL e valor valor valorNULL Atrás
  49. 49. 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 permite- nos remover um nó qualquer da lista em tempo constante, usando apenas um ponteiro para essa célula.
  50. 50. Listas duplamente ligadas -operações 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 conteúdo da lista. procura(): devolve a posição de um determinado elemento. remove(): remove o elemento desejado da lista.
  51. 51. Lista duplamente Ligada -operações#include<stdio.h>#include<stdlib.h>typedef struct listadl{ LDL *prox; //próximo elemento da lista LDL *ant; //elemento anterior da lista int dado;}LDL;
  52. 52. Lista duplamente Ligada -operaçõesDevolve uma lista com elemento elem.LDL *cria(int elem){ LDL*tmp; tmp =(LDL *)malloc(sizeof(tmp)); tmp->dado = elem; tmp->prox = NULL; tmp->ant = NULL; return tmp;}
  53. 53. Lista duplamente Ligada -operaçõesInsere o elemento elem na lista L.void *insere (int elem, LDL *L){ LDL*tmp; tmp=L; while((tmp->prox ==NULL)==0) tmp = tmp->prox; tmp->prox = cria(elem); (tmp->prox)-> ant = tmp;}
  54. 54. Lista duplamente Ligada -operaçõesImprime todos os elementos da if((no1 == NULL)==0)lista. printf(“O proximo:%d”,void *mostra(LDL *L){ no1->dado); LDL*tmp, *no1, *no2; else tmp=L; printf(“Não possui while((tmp->prox próximo.”);==NULL)==0){ if((no2 == NULL)==0) no1 = tmp->prox; printf(“O anterior:%d”, no2 = tmp->ant; no2->dado); printf(“O elemento: %d”, else printf(“Não possuitmp->dado); anterior.”); tmp= tmp->prox; } }
  55. 55. Lista duplamente Ligada -operaçõesRemove o elemento elem da lista L. elseLDL *remove(int elem, LDL *L){ if((proxno == NULL)==0){ LDL*tmp, *proxno, *noant; proxno->ant =NULL; tmp=L; while((tmp->prox ==NULL)==0){ free(tmp); proxno = tmp->prox; L = proxno; noant = tmp->ant; return L; if((tmp->dado == elem) } if((noant == NULL)==0) else { if((proxno == NULL)==0){ L = NULL; noant->prox =proxno; free(tmp); proxno->ant =noant; return L; free(tmp); } return L; else{ } tmp = tmp -> prox; else{ } noant->prox=NULL; printf(“O elemento %d não se free(tmp); return L; encontra na listan”, elem); } }
  56. 56. Lista duplamente Ligada -operaçõesDevolve a posição do elemento elem.int *procura(int elem, LDL *L){ LDL*tmp; int pos = 0; tmp=L; while((tmp->prox ==NULL)==0) if((tmp->dado) == elem){ printf(“A posição do elemento %d é %dn”, elem, pos); return pos; } else{ pos++; tmp = tmp->prox; } printf(“O elemento não se encontra na lista”);}
  57. 57. Lista duplamente Ligada -operaçõesDevolve o elemento anterior a elem.void *anterior(int elem, LDL *L){ LDL*tmp, *noant; tmp=L; while((tmp==NULL)==0) if((tmp->dado) == elem){ noant = tmp->ant; printf(“O elemento anterior a %d é %dn”, elem, noant->dado); else printf(“Não existe anterior”); exit(); } else tmp = tmp->prox; printf(“O elemento não se encontra na lista”);}
  58. 58. Lista Circular Ligada As listas circulares são 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 situações do tipo FIFO (First In First Out).Fig. 1 Lista circular ligada ( lista simplesmente ligada) dado dado dado dadoFig. 2 Lista circular ligada ( lista duplamente ligada) dado dado dado dado
  59. 59. Lista circular Ligada -operaçõestypedef struct no{ 3. Aloca e desaloca nó. int dado; int aloca(LCSL *lista, int valor){ struct no *prox;}NO, LCSL; LCSL *tmp;NO cabeca; tmp= (LCSL) malloc(sizeof(no));1. Inicializa a lista circular if (tmp==NULL)int inicializa(LCSL *cabeca) return 1; // Erro …{ *lista = tmp; *cabeca=NULL; tmp->dado = valor; return 0;} tmp->prox = NULL;2. Verifica se a lista está cheia return 0; }int vazia(LCSL lista){ void liberta(LCSL *lista){ return (lista==NULL)? 1:0; free(*lista);} *lista=NULL; }
  60. 60. Lista circular Ligada -operações4. Inserção de um item 5. Adiciona um item ao fim daint insere(LCSL *lista, int valor) lista.{ int adiciona(LCSL *lista, int LCSL tmp; valor){ if (aloca(&tmp, valor)== 1) LCSL tmp; return 1; // Erro if (aloca(&tmp, valor)== 1) if (vazia(*lista) == 1){ return 1; // Erro //True=1 if (vazia(*lista) == 1) //True=1 tmp->prox = tmp; tmp->prox = tmp; *lista=tmp; else{ } else { tmp->prox = *lista->prox; *lista->prox = tmp; tmp->prox = *lista->prox; } *lista->prox = tmp; *lista = tmp; } return 0; //Ok return 0;} }
  61. 61. Lista circular Ligada -6. Eliminação de um nóoperaçõesint apagaNo(LCSL *lista, LCSL no){ LCSL tmp; if(vazia(*lista) == 1) return 1; if(no==no->prox) *lista=NULL; else { for(tmp=*lista->prox; tmp!=*lista && tmp->prox!=no; tmp=tmp- >prox) ; if(tmp->prox !=no) return 1; tmp->prox = no->prox; if(no== *lista) *lista=tmp; } free(&no); return 0;}
  62. 62. Pilha e Fila1. Pilha • Implementação sequencial da pilha2. Fila I. Operações básicas II. Implementação sequencial da fila
  63. 63. Pilhas Pilhas são listas onde a inserção ou a remoção de um item é feita no topo. Definição: 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 são conhecidas como listas LIFO (last in first out)
  64. 64. Implementação de pilhas Como lista sequencial ou ligada? ◦ No caso geral de listas ordenadas, a maior vantagem da alocação dinâmica sobre a sequencial, se a memória não for problema, é a eliminação de deslocamento na inserção ou eliminação dos elementos. No caso das pilhas, essas operações de deslocamento não ocorrem. Portanto, alocação 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 não é conhecido
  65. 65. Pilha
  66. 66. Implementação Sequencial#define MAXPILHA 50#define tpilha(p) (p->topo – p->base)typedef struct pilha{ int base[MAXPILHA]; int *topo;}PILHA;
  67. 67. Implementação Sequencial1. Inicializa a pilha vazia int inicializa(PILHA *pp){ pp->topo = pp->base; return 1; }2. Verifica se pilha está vazia int vazia(PILHA *pp){ return (pp->topo == pp->base) ? 1 : 0; }3. Coloca dado na pilha. Apresenta erro se não haver espaço. int push(PILHA *pp, int dado){ if(tpilha(pp) == MAXPILHA) return 0; *pp->topo=dado; pp->topo++; return 1; }
  68. 68. Implementação Sequencial4. 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; }5. 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) }
  69. 69. 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.
  70. 70. FilaUma 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 (não pode guardar mais elementos). ◦ vazia – Verificar se a fila está vazia (não contém elementos) Inicializar ou limpar: ◦ inicializa – Colocar a fila num estado “pronto” a ser utilizada
  71. 71. Fila – Outras Operações Buscar por elementos que coincidam com certo padrão (casamento de padrão) Ordenar uma lista Intercalar duas listas Concatenar duas listas Dividir uma lista em duas Fazer cópia da lista
  72. 72. Fila A implementação de uma fila pode ser efectuada através da utilização de diferentes estruturas de dados (vectores, listas ligadas, árvores, etc.). De seguida, apresenta-se duas implementação de uma fila através da utilização de vectores e listas ligadas.
  73. 73. Fila Características das filas: ◦ Os dados são 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) Implementação das filas: ◦ usando vectores/arrays (circulares ou não) ◦ utilizando um apontador para nós de
  74. 74. Fila Implementação em C usando arrays Vantagens: ◦ Facilidade de implementação. Desvantagens: ◦ Vectores possuem um espaço limitado para armazenamento de dados. ◦ Necessidade de definir um espaço grande o suficiente para a fila ◦ Necessidade de um indicador para o inicio e para o fim da fila Método de Implementação ◦ A adição de elementos à fila resulta no incremento do indicador do fim da fila ◦ A remoção de elementos da fila resulta no incremento do indicador do inicio da fila
  75. 75. Implementação de Filas em C Usamos o vector para armazenar os elementos da fila e duas variáveis, inic e fim, para armazenar as posições 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.
  76. 76. Capitulo III: Árvores Docente: Dikiefu Fabiano, Msc
  77. 77. Sumário3.1 Introdução3.2 Tipos de árvores3.3 Árvores binárias3.3.1 Estrutura de uma árvore binária3.3.2 Descrição3.3.3 Altura3.3.4 Representação em C3.4 Árvores Genéricas3.4.1 Estrutura de uma árvore genérica3.4.2 Representação em C3.4.3 Problemas com Representação
  78. 78. Introdução As estruturas anteriores são chamadas de unidimensionais (ou lineares) ◦ Exemplo são listas sequenciais e ligadas Não podem ser usadas como hierarquias. ◦ Exemplo: árvore de directórios Árvore é uma estrutura adequada para representar hierarquias A forma mais natural para definirmos uma estrutura de árvore é usando
  79. 79. Estrutura de Árvores Uma árvore é composta por um conjunto de nós. Existe um nó r, denominado raiz, que contém zero ou mais sub-árvores, cujas raízes são ligadas a r. Esses nós raízes das sub-árvores são ditos filhos do nó pai, no caso r. Nós com filhos são chamados nós internos e nós que não têm filhos são chamados folhas, ou nós externos. É tradicional desenhar as árvores com a raiz para cima e folhas para baixo, ao contrário do que seria de se esperar.
  80. 80. Estrutura de Árvores Por adoptarmos essa forma de representação gráfica, não representamos explicitamente a direcção dos ponteiros, subentendendo que eles apontam sempre do pai para os filhos.
  81. 81. Tipos de Árvores O número de filhos permitido por nó e as informações armazenadas em cada nó diferenciam os diversos tipos de árvores existentes. Existem dois tipos de árvores: ◦ árvores binárias, onde cada nó tem, no máximo, dois filhos. ◦ árvores genéricas, onde o número de filhos é indefinido. Estruturas recursivas serão usadas como base para o estudo e a implementação das operações com árvores.
  82. 82. Árvore Binária Um exemplo de utilização de árvores binárias está na avaliação de expressões. Como trabalhamos com operadores que esperam um ou dois operandos, os nós da árvore para representar uma expressão têm no máximo dois filhos. Nessa árvore, os nós folhas representam operandos e os nós internos operadores.
  83. 83. Árvores BináriasEsta árvore representa a expressão: (3+6)*(4-1)+5
  84. 84. Estrutura de uma AB Numa árvore binária, cada nó tem zero, um ou dois filhos. De maneira recursiva, podemos definir uma árvore binária 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).
  85. 85. Estrutura de uma AB • Os nós a, b, c, d, e, f formam uma árvore binária da seguinte maneira: • A árvore é composta do nó a, da subárvore à esquerda formada por b e d, e da sub- árvore à direita formada por c, e e f. • O nó a representa a raiz da árvore e os nós b e c as raízes das sub-árvores. • Finalmente, os nós d, e e f são folhas da árvore.
  86. 86. Descrição de AB Para descrever árvores binárias, podemos usar a seguinte notação textual: ◦ A árvore vazia é representada por <>, ◦ e árvores não vazias por <raiz sae sad>. Com essa notação, a árvore da Figura anterior é representada por: ◦ <a<b<><d<><>>> <c<e<><>><f<><>>>>.
  87. 87. Descrição de AB Uma sub-árvore de uma árvore binária é sempre especificada como sendo a sae ou a sad de uma árvore maior, e qualquer das duas sub- árvores pode ser vazia. As duas árvores da Figura abaixo são distintas.
  88. 88. 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.
  89. 89. Representação em C Podemos definir um tipo para representar uma árvore binária. Vamos considerar que a informação a ser armazenada são valores de caracteres simples. Vamos inicialmente discutir como podemos representar uma estrutura de árvore binária em C. Que estrutura podemos usar para representar um nó da árvore? Cada nó deve armazenar três informações: a informação propriamente dita, no caso um caractere, e dois ponteiros para as sub- árvores, à esquerda e à direita.
  90. 90. Representação 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 oprimeiro nó, a estrutura da árvore como umtodo é representada por um ponteiro para onó raiz.
  91. 91. 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; }
  92. 92. Árvore Vazia int vazia(Arv* a) { return a==NULL; }
  93. 93. Exemplo Exemplo: Usando as operações inicializa e cria, crie uma estrutura que represente a seguinte árvore.
  94. 94. 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 );
  95. 95. ExemploAlternativamente, a árvore poderia ser criadarecursivamente com uma única atribuição,seguindo a sua estrutura.Como isso pode ser feito?
  96. 96. ExemploArv* a = cria(a, cria(b, inicializa(), cria(d, inicializa(), inicializa()) ), cria(c, cria(e, inicializa(), inicializa()), cria(f, inicializa(), inicializa()) ) );
  97. 97. Exibe Conteúdo da Árvorevoid 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 exibição?E para exibir na forma in-fixada? E na pós- fixada?
  98. 98. Liberando MemóriaArv* libera (Arv* a){ if (!vazia(a)){ libera(a->esq); /* libera sae */ libera(a->dir); /* libera sad */ free(a); /* libera raiz */ } return NULL;}
  99. 99. Criação e LiberaçãoVale a pena notar que a definição deárvore, por ser recursiva, não fazdistinção entre árvores e sub-árvores.Assim, cria pode ser usada paraacrescentar uma sub-árvore em umramo de uma árvore, e libera pode serusada para remover uma sub-árvorequalquer de uma árvore dada.
  100. 100. Criação e LiberaçãoConsiderando a criação da árvore feita anteriormente,podemos acrescentar alguns nós, 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 exercício a verificação do resultadofinal dessas operações.
  101. 101. Buscando um Elemento Essa função tem como retorno um valor booleano (um ou zero) indicando a ocorrência ou não do carácter na árvore. int busca (Arv* a, char c){ if (vazia(a)) return 0; /* não encontrou */ else return a->info==c ||busca(a->esq,c) ||busca(a->dir,c); }
  102. 102. Buscando um Elemento Podemos dizer que a expressão: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);
  103. 103. Árvores Genéricas Como vimos, numa árvore binária o número de filhos dos nós é limitado em no máximo dois. No caso da árvore genérica, esta restrição não existe. Cada nó pode ter um número arbitrário de filhos. Essa estrutura deve ser usada, por exemplo, para representar uma árvore de directórios. Supor que não há árvore vazia.
  104. 104. Exemplo
  105. 105. Representação em C Devemos levar em consideração o número de filhos que cada nó pode apresentar. Se soubermos que numa aplicação o número máximo de filhos é 3, podemos montar uma estrutura com 3 campos apontadores, digamos, f1, f2 e f3. Os campos não 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.
  106. 106. Representação em CPrevendo um número máximo de filhosigual a 3,e considerando a implementação deárvores paraarmazenar valores de caracteres simples,a declaração do tipo que representa o nóda árvore poderia ser: struct arv3 { char val; struct arv3 *f1, *f2, *f3; };
  107. 107. Exemplo
  108. 108. Problemas comRepresentação Como se pode ver no exemplo, em cada um dos nós que tem menos de três filhos, o espaço correspondente aos filhos inexistentes é desperdiçado. Além disso, se não existe um limite superior no número de filhos, esta técnica pode não ser aplicável. O mesmo acontece se existe um limite no número de nós, mas esse limite será raramente alcançado, pois estaríamos tendo um grande desperdício de espaço de memória com os campos não utilizados. Há alguma solução para contornar tal problema?
  109. 109. SoluçãoUma solução que leva a um aproveitamento melhor doespaço utiliza uma “lista de filhos”: um nó aponta apenaspara seu primeiro (prim) filho, e cada um de seus filhos,excepto o último, aponta para o próximo (prox) irmão.A declaração de um nó pode ser: struct arvgen { char info; struct arvgen *prim; struct arvgen *prox; }; typedef struct arvgen ArvGen;
  110. 110. Exemplo da Solução
  111. 111. Exemplo da Solução Uma das vantagens dessa representação é que podemos percorrer os filhos de um nó de forma sistemática, de maneira análoga ao que fizemos para percorrer os nós de uma lista simples. Com o uso dessa representação, a generalização da árvore é apenas conceitual, pois, concretamente, a árvore foi transformada em uma árvore binária, com filhos esquerdos apontados por prim e direitos apontados por prox.
  112. 112. Criação de uma ÁrvoreGenArvGen* cria (char c){ ArvGen *a =(ArvGen *)malloc(sizeof(ArvGen)); a->info = c; a->prim = NULL; a->prox = NULL; return a;}
  113. 113. Inserção Como não vamos atribuir nenhum significado especial para a posição de um nó filho, a operação de inserção pode inserir a sub- árvore em qualquer posição. Vamos optar por inserir sempre no início 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; }
  114. 114. Exemplo Criação ÁrvoreGen Com as funções cria e insere como construir a árvore abaixo?
  115. 115. Exemplo Criação ÁrvoreGen /* cria nós como folhas */ ArvGen* a = cria(a); /* monta a hierarquia */ ArvGen* b = cria(b); insere(c,d); ArvGen* c = cria(c); insere(b,e); ArvGen* d = cria(d); insere(b,c); ArvGen* e = cria(e); insere(i,j); ArvGen* f = cria(f); insere(g,i); ArvGen* g = cria(g); insere(g,h); ArvGen* h = cria(h); insere(a,g); ArvGen* i = cria(i); insere(a,f); ArvGen* j = cria(j); insere(a,b); ...
  116. 116. Impressão (pré-ordem)void imprime (ArvGen* a){ ArvGen* p; printf("%cn",a->info); for (p=a->prim; p!=NULL; p=p- >prox) imprime(p);}
  117. 117. Busca de Elementoint 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;}
  118. 118. Liberação de MemóriaO único cuidado que precisamostomar na programação dessa funçãoé a de liberar as subárvores antesde liberar o espaço associado a umnó (isto é, usar pós-ordem).
  119. 119. Liberação de Memória void libera (ArvGen* a){ ArvGen* p = a->prim; while (p!=NULL) { ArvGen* t = p->prox; libera(p); p = t; } free(a); }
  120. 120. Capitulo IV - Ordenação Docente: Dikiefu Fabiano, Msc
  121. 121. Sumário4. Ordenação Interna 4.1 Ordenação por Selecção 4.2 Ordenação por Inserção 4.3 Shellsort 4.4 Quicksort 4.5 Heapsort 121
  122. 122. Conceitos Básicos  Ordenar: processo de rearranjar um conjunto de objectos em uma ordem ascendente ou descendente.  A ordenação visa facilitar a recuperação posterior de itens do conjunto ordenado.  A maioria dos métodos de ordenação é baseada em comparações das chaves.  Existem métodos de ordenação que utilizam o princípio da distribuição.. 122
  123. 123. Conceitos Básicos Notação utilizada nos algoritmos:• Os algoritmos trabalham sobre os registos de um arquivo.• Cada registo possui uma chave utilizada para controlar a ordenação.• Podem existir outros componentes em um registo
  124. 124. Conceitos básicosUm método de ordenação é estável se a ordemrelativa dos itens com chaves iguais não se alteradurante a ordenação.• Alguns dos métodos de ordenação mais eficientesnão são estáveis.• A estabilidade pode ser forçada quando o método énão-estável.• Sedgewick (1988) sugere agregar um pequeno índicea cada chave antes de ordenar, ou então aumentar achave de alguma outra forma. 124
  125. 125. Classificação dos métodos deordenação1. Ordenação interna: arquivo a ser ordenado cabe todo na memória principal.2. Ordenação externa: arquivo a ser ordenado não cabe na memória principal.Diferenças entre os métodos: Em um método de ordenação interna, qualquer registo pode ser imediatamente cessado. Em um método de ordenação externa, os registos são cessados sequencialmente ou em grandes blocos. 125
  126. 126. Classificação dos métodos de ordenaçãointerna Métodos simples: • Adequados para pequenos arquivos. • Produzem programas pequenos. Métodos eficientes: • Adequados para arquivos maiores. • Usam menos comparações. • As comparações são mais complexas nos detalhes. Métodos simples são mais eficientes para pequenos arquivos. 126
  127. 127. Ordenação por SelecçãoUm dos algoritmos mais simples de ordenação. Algoritmo:  Seleccione o menor item do vector.  Troque-o com o item da primeira posição do vector.  Repita essas duas operações 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. 127
  128. 128. Ordenação por SelecçãoVantagens:  Custo linear no tamanho da entrada para o número 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 não ajuda em nada, pois o custo continua quadrático.  O algoritmo não é estável. 128
  129. 129. Ordenação por Selecçãovoid selectionSort(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; }} 129
  130. 130. Código da ordenação SelectionSort com stringsvoid ordenar_seleccao() { 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); } } }}
  131. 131. Bubble Sort O bubble sort, ou ordenação por flutuação (literalmente "por bolha"), é um algoritmo de ordenação dos mais simples. A ideia é percorrer o vector diversas vezes, a cada passagem fazendo flutuar para o topo o maior elemento da sequência. Essa movimentação lembra a forma como as bolhas em um tanque de água procuram seu próprio nível, e disso vem o nome do algoritmo
  132. 132. void bubble( 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; } } }}
  133. 133. void swapbubble( int v[], int i ) { aux = v[i]; v[i] = v[i+1]; v[i+1]=aux;}void bubble( 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] ) { swapbubble( v, i ); } } }}
  134. 134.  Método de ordenação Bolha com ordenação de strings.void bubble(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);}
  135. 135. Ordenação por Inserção Método preferido dos jogadores de cartas. Algoritmo: • Em cada passo a partir de i =2 faça: • Seleccione o i-ésimo item da sequência fonte. • Coloque-o no lugar apropriado na sequência destino de acordo com o critério de ordenação.• As chaves em negrito representam a sequência destino. 135
  136. 136. Ordenação por Inserçãovoid 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 colocação do item no seu lugar apropriado nasequência destino é realizada movendo-se itens comchaves maiores para a direita e então inserindo o itemna posição deixada vazia. 136
  137. 137. Ordenação por InserçãoConsiderações sobre o algoritmo:  O processo de ordenação pode ser terminado pelas condições: • Um item com chave menor que o item em consideração é encontrado. • O final da sequência destino é atingido à esquerda.  Solução: • Utilizar um registo sentinela na posição zero do vector. 137
  138. 138. Ordenação por Inserção O número mínimo de comparações e movimentos ocorre quando os itens estão originalmente em ordem. O número máximo ocorre quando os itens estão originalmente na ordem reversa. É o método a ser utilizado quando o arquivo está “quase” ordenado. É um bom método quando se deseja adicionar uns poucos itens a um arquivo ordenado, pois o custo é linear. O algoritmo de ordenação por inserção é estável. 138
  139. 139. ShellsortProposto por Shell em 1959.  É uma extensão do algoritmo de ordenação por inserção.  Problema com o algoritmo de ordenação por inserção: • Troca itens adjacentes para determinar o ponto de inserção. • São efectuadas n − 1 comparações e movimentações quando o menor item está na posição mais à direita no vector.  O método de Shell contorna este problema permitindo trocas de registos distantes um do outro. 139
  140. 140. Shellsort  Os itens separados de h posições são rearranjados.  Todo h-ésimo item leva a uma sequência ordenada.  Tal sequência é dita estar h-ordenada.  Exemplo de utilização:• Quando h = 1 Shellsort corresponde ao algoritmo de 140
  141. 141. Shellsort• Como escolher o valor de h:  Sequência 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 sequência é difícil de ser batida por mais de 20% em eficiência.  A sequência para h corresponde a 1, 4, 13, 40, 121, 364, 1.093, 3.280, . . . 141
  142. 142. Shellsortvoid 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 implementação do Shellsort não utiliza registos sentinelas.  Seriam necessários h registos sentinelas, uma para cada h-ordenação. 142
  143. 143. Shellsort Vantagens: • Shellsort é uma óptima opção para arquivos de tamanho moderado. • Sua implementação é simples e requer uma quantidade de código pequena. Desvantagens: • O tempo de execução do algoritmo é sensível à ordem inicial do arquivo. • O método não é estável, 143
  144. 144. Quicksort Proposto por Hoare em 1960 e publicado em 1962. É o algoritmo de ordenação interna mais rápido que se conhece para uma ampla variedade de situações. Provavelmente é o mais utilizado. A ideia básica é dividir o problema de ordenar um conjunto com n itens em dois problemas menores. Os problemas menores são ordenados independentemente. Os resultados são combinados para produzir a solução final. 144
  145. 145. Quicksort A parte mais delicada do método é relativa ao método partição. O vector v[esq..dir ] é rearranjado por meio da escolha arbitrária 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. 145
  146. 146. QuicksortAlgoritmo 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] são menores ou iguais a x. • Os itens em v[i], v[i + 1], . . . , v[dir ] são maiores ou iguais a x. 146
  147. 147. Quicksort• Ilustração do processo de partição:  O pivô x é escolhido como sendo v[(i+j) / 2].  Como inicialmente i = 1 e j = 6, então x = v[3] = D.  Ao final do processo de partição i e j se cruzam em i = 3e j = 2. 147
  148. 148. Quicksortvoid 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; } 148
  149. 149. QuicksortMétodo 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); }} 149
  150. 150.  Implementação 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;
  151. 151. } 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);}
  152. 152. Lembrando que quando você for chamara função recursiva terá que chamar amesma da seguinte formaordenar_quicksort(0,n-1). O 0(zero)serve para o início receber a posiçãozero do vector e o fim será o tamanhodo vector -1.
  153. 153. 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--; } }
  154. 154. if (f>ini) ordenar_quicksort_nome(ini,f); if (i<fim) ordenar_quicksort_nome(i,fim);}
  155. 155. QuicksortExemplo do estado do vector em cada chamadarecursiva do procedimento Ordena:• O pivô é mostrado em negrito. 155
  156. 156. Quicksort Vantagens: • É extremamente eficiente para ordenar arquivos de dados. • Necessita de apenas uma pequena pilha como memória auxiliar. Desvantagens: • Sua implementação é muito delicada e difícil: Um pequeno engano pode levar a efeitos inesperados para algumas entradas de dados. • O método não é estável. 156
  157. 157. Heapsort Possui o mesmo princípio de funcionamento da ordenação por selecção. Algoritmo: 1. Seleccione o menor item do vector. 2. Troque-o com o item da primeira posição do vector. 3. Repita estas operações 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 comparações. Isso pode ser reduzido utilizando uma fila de prioridades. 157
  158. 158. Heaps É uma sequência 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 definição pode ser facilmente visualizada em uma árvore binária completa: árvore binária completa: • Os nós são numerados de 1 a n. • O primeiro nó é chamado raiz. • O nó k/2 é o pai do nó k, para 1 < k n. • Os nós 2k e 2k + 1 são os filhos à esquerda e à direita do nó k, para 1 k k/2 158
  159. 159. Heaps As chaves na árvore satisfazem a condição do heap. As chaves em cada nó são maiores do que as chaves em seus filhos. A chave no nó raiz é a maior chave do conjunto. Uma árvore binária completa pode ser representada por um arranjo: A representação é extremamente compacta. Permite caminhar pelos nós da árvore facilmente. Os filhos de um nó i estão nas posições 2i e 2i + 1. O pai de um nó i está na posição i/2. 159
  160. 160. Heaps Na representação do heap em um arranjo, a maior chave está sempre na posição 1 do vector. Os algoritmos para implementar as operações 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 não necessita de nenhuma memória 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 não existem dois índices i e j tais que j = 2i ou j = 2i + 1. 160
  161. 161. Heapsortvoid 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]; } 161
  162. 162. 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; }} 162
  163. 163. HeapsortAlgoritmo:  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 condição de heap é violada: • O heap é refeito trocando os itens D e S.  O item R é incluindo no heap (esq = 2), o que não viola a condição de heap.  O item O é incluindo no heap (esq = 1).  A Condição de heap violada: • O heap é refeito trocando os itens O e S, encerrando o processo. 163
  164. 164. HeapsortExemplo da operação de aumentar o valor da chave do item na posição i:O tempo de execução do procedimento AumentaChave em um item doheap é O(log n). 164
  165. 165. Construção de HeapAlgoritmo:1. Construir o heap.2. Troque o item na posição 1 do vector (raiz do heap) com o item da posição 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.
  166. 166. HeapsortExemplo de aplicação do Heapsort : 1 2 3 4 5 6 7 S R O E N A D R N O E D A S O N A E D R N E A D O E D A N D A E A D• O caminho seguido pelo procedimento Refaz para reconstituir a condição do heap está em negrito.• Por exemplo, após a troca dos itens S e D na segunda linha da Figura, o item D volta para a posição 5, após passar pelas posições 1 e 2.
  167. 167. Referências1. Levitin, Anany “Introduction to the design and analysis of algorithm” Addison-Wesley 20032. Pedro Neto, João “Programação, algoritmo e estrutura de dados” escola editora 20083. Damas, Luís “Programação em C” …4. Ziviani, Nivio e Botelho, Fabiano Cupertino, “Projecto de Algoritmos com implementação em Pascal e C” editora Thomson, 20075. http://www.icmc.usp.br/~sce182/arvore.html6. http://w3.ualg.pt/~hshah/ped7. http://www.liv.ic.unicamp.br/~bergo/mc202/

×