You are on page 1of 10

PESQUISA DE DADOS Como recuperar uma informao a partir de uma grande massa de informao previamente armazenada?

A informao dividida em registros, e cada registro possui uma chave para ser usada na pesquisa. O objetivo da pesquisa encontrar uma ou mais ocorrncias de registros com chaves iguais chave de pesquisa. Neste caso, ter ocorrido uma pesquisa com sucesso; caso contrrio a pesquisa ter sido sem sucesso. Um conjunto de registros chamado de tabela ou arquivo. Tabela entidades de vida curta, criadas na memria interna durante a execuo de um programa. Arquivo entidades de vida mais longa, armazenadas em memria externa. Essa distino entre tabela e arquivo no precisa, podemos ter um arquivo de ndices que pode ser tratado como uma tabela, enquanto uma tabela de valores de funes que pode ser tratada como um arquivo. Existe uma variedade enorme de mtodos de pesquisa. A escolha do mtodo mais adequado a determinada aplicao depende principalmente: - Da quantidade dos dados envolvidos; - De o arquivo estar sujeito a inseres e retiradas frequentes, ou de o contedo do arquivo ser praticamente estvel (neste caso, importante minimizar o tempo de pesquisa, Sem preocupao com o tempo necessrio para estruturar o arquivo). PESQUISA SEQUENCIAL O mtodo de pesquisa mais simples que existe, funciona da seguinte forma: a partir do primeiro registro, pesquise sequencialmente at encontrar a chave procurada. Uma forma possvel de armazenar um conjunto de registros por meio do tipo estruturado arranjo. Cada registro contm um campo chave que identifica o registro Exemplo #define MAXN 10 typedef long TipoChave; typedef struct TipoRegistro{ TipoChave Chave; /*outros componentes*/ }TipoRegistro; typedef int TipoIndice; typedef struct TipoTabela { TipoRegistro Item[MAXN + 1]; TipoIndice n; }TipoTabela; Alm da chave, podem existir outros componentes em um registro, os quais no tem influncia muito grande nos algoritmos. Uma possvel implementao para as operaes Inicializa, Pesquisa e Insere mostrado nos prximos slides: void Inicializa(TipoTabela *T) { T->n=0; } TipoIndice Pesquisa(TipoChave x, TipoTabela *T)

{ int i; T->Item[0].Chave = x; i = T->n + 1; do{ i--; }while(T->Item[i].Chave != x); return i; } void Insere(TipoRegistro Reg, TipoTabela *T) { if(T->n == MAXN) printf("Erro : tabela cheia\n"); else{ T->n++; T->Item [T->n] = Reg; } } A funo Pesquisa retorna o ndice do registro que contm a chave x; caso no esteja presente, o valor retornado zero. Observe que esta implementao no suporta mais de um registro com uma mesma chave. Em aplicaes com esta caracterstica, necessrio incluir um argumento a mais na funo Pesquisa para conter o ndice a partir do qual se quer pesquisar. Um registro sentinela contendo a chave de pesquisa colocado na posio zero do array. Essa tcnica garante que a pesquisa sempre termina. Aps a chamada da funo Pesquisa, se o ndice zero, significa que a pesquisa foi sem sucesso. Para uma pesquisa com sucesso temos: Melhor caso: C(n) = 1 Pior caso: C(n) = n Caso mdio: C(n) = (n + 1)/2 O melhor caso ocorre quando o registro procurado o primeiro pesquisado. O pior caso ocorre quando o registro o ltimo pesquisado ou ento no est presente no arquivo, para tal, necessrio realizar n comparaes. Para o caso mdio, vamos considerar que toda pesquisa recupera um registro, no existindo, portanto, pesquisa sem sucesso. Para uma pesquisa sem sucesso temos: C(n) = n + 1 Observe que o looping interno da funo. Pesquisa, visto na implementao em C, extremamente simples: o ndice i decrementado e a chave de pesquisa comparada com a chave que est no registro. Por essa razo, esta tcnica usando sentinela conhecida por pesquisa sequencial rpida. Esse algoritmo a melhor soluo para o problema de pesquisa em tabelas com registros ou menos.

PESQUISA DE DADOS BINRIA A pesquisa em uma tabela pode ser muito mais eficiente se os registros forem mantidos em ordem. Para saber se uma chave est presente na tabela, compare a chave com o registro que est na posio do meio da tabela. Se a chave menor, ento o registro procurado est na primeira metade da tabela. Se a chave maior, ento o registro procurado est na segunda metade da tabela. Repita o processo at que a chave seja encontrada ou fique apenas um registro cuja chave diferente da procurada, indicando uma pesquisa sem sucesso. A cada iterao do algoritmo de pesquisa binria o tamanho da tabela dividido ao meio. O nmero de vezes que o tamanho da tabela dividido ao meio cerca de log n. O custo para manter a tabela ordenada alto. Cada insero na posio p da tabela implica o deslocamento dos registros a partir da posio p para as posies seguintes. A pesquisa binria no deve ser usada em aplicaes muito dinmicas. EXEMPLO 1. int busca_bin(int n, int* vet, int elem) 2. { 3. int ini=0; 4. int fim=n-1; 5. int meio; 6. while(ini<=fim){ 11 7. meio=(ini+fim)/2; 8. if(elem<vet[meio]) 9. fim=meio-1; 10. else if(elem>vet[meio]) 11. ini=meio+1; 12. else 13. return meio; 14. } 15. return -1; 16. } O desempenho desse algoritmo muito superior ao de busca sequencial. O pior caso caracterizase pela situao de o elemento que buscamos no estar no vetor. O algoritmo de busca binria consiste em repetir o mesmo procedimento recursivamente, o qual pode ser implementado de forma recursiva. Embora a implementao no recursiva seja mais eficiente e mais adequada para esse algoritmo, implementao recursiva mais sucinta. Na implementao recursiva, temos dois casos a serem tratados: 1. A busca deve continuar na primeira metade do vetor, logo chamamos a funo recursivamente passando como parmetros o nmero de elementos dessa primeira parte restante e o mesmo ponteiro para o primeiro elemento, pois a primeira parte tem o mesmo primeiro elemento do que o vetor com um todo. 2. A busca deve continuar apenas na segunda parte do vetor, logo passamos na chamada recursiva, alm do nmero de elementos restantes, um ponteiro para o primeiro elemento dessa segunda parte.

EXEMPLO int pertence_rec(int n, int* vet, int elem) { if(n<=0) return 0; else{ int meio=n/2; 18 if(elem<vet[meio]) return pertence_rec(meio, vet, elem); else if(elem>vet[meio]) return pertence_rec(n-1-meio, &vet[meio+1], elem); else return 1; } } O exemplo anterior informa apenas se o elemento pertence ou no ao vetor, e tem como valor de retorno falso (0) ou verdadeiro (1). Se quisermos que a funo tenha como valor de retorno o ndice do elemento, devemos acertar o valor retornando pela chamada recursiva na segunda parte do vetor. EXEMPLO 1. int busca_bin_rec(int n, int* vet, int elem) 2. { 3. if(n<=0) 4. return -1; 21 5. else{ 6. int meio=n/2; 7. if(elem<vet[meio]) 8. return busca_bin_rec(meio, vet, elem); 9. else if(elem>vet[meio]) 10. { 11. int r=busca_bin_rec(n-1-meio, &vet[meio+1], elem); 12. if(r<0) 13. return -1; 14. else 22 15. return meio+1+r; 16. } 17. else 18. return meio; 19. } 20. } Se tivermos os dados armazenados em uma lista encadeada, s temos a alternativa de implementar um algoritmo de busca linear, mesmo se os elementos estiverem ordenados. A lista encadeada no uma boa opo para estruturar nossos dados, se desejarmos realizar muitas operaes de busca. A estrutura dinmica apropriada para realizao de busca a rvore binria de busca, que ser estudado mais adiante.

MTODOS DE ORDENAO: SELEO E TROCA. Ordenar corresponde ao processo de rearranjar um conjunto de objetos em ordem ascendente ou descendente. O objetivo principal da ordenao facilitar a recuperao posterior de itens do conjunto ordenado. Os mtodos de ordenao so classificados em dois grandes grupos. Se o arquivo a ser ordenado cabe todo na memria principal, ento o mtodo de ordenao chamado ordenao interna. Se o arquivo a ser ordenado no cabe na memria principal e, por isso, tem de ser armazenado em fita ou disco, ento o mtodo de ordenao chamado ordenao externa. Principais mtodos de ordenao utilizando os princpios de comparao de chaves e distribuio. ORDENAO POR SELEO O principio de funcionamento da ordenao por seleo o seguinte: selecione o menor item do vetor e a seguir troque-o com o item que est na primeira posio do vetor. Repita essas duas operaes com os n 1 itens restantes, depois com os n 2 itens, at que reste apenas um elemento. EXEMPLO 1. void Selecao(TipoItem *A, TipoIndice n) 2. { 3. TipoIndice i, j, Min; 4. TipoItem x; 5. for(i=1; i<=n-1; i++) 6. { 9 7. Min=i; 8. for(j=i+1; j<=n; j++) 9. if(A[j].Chave<A[Min].Chave) 10. Min = j; 11. x=A[Min]; 12. A[Min]=A[i]; 13. A[i]=x; 14. } 15. } O algoritmo de ordenao por seleo um dos mtodos de ordenao mais simples que existem. O tempo de execuo linear no tamanho da entrada, o que muito difcil de ser alcanado por qualquer outro mtodo. Este algoritmo deve ser utilizado para arquivos com registros muito grandes. Ordenao Bolha ou BUBBLESORT A idia fundamental fazer uma srie de comparaes entre os elementos do vetor. Quando dois elementos esto fora de ordem, h uma inverso, e esses dois elementos so trocados de posio, ficando em ordem correta. O primeiro elemento comparado com o segundo. Se uma inverso for encontrada, a troca feita. Independente se houve ou no troca aps a primeira comparao, o segundo elemento comparado com o terceiro, e, caso uma inverso seja encontrada, a troca feita. O processo continua at que o penltimo elemento seja comparado com o ltimo.

Com isso, o elemento de maior valor do vetor levado para a ltima posio. EXEMPLO 1. void bolha(int n, int* v) 2. { 3. int i, j; 4. for(i=n-1; i>=1; i--) 5. for(j=0; j<i; j++) 15 6. if(v[j]>v[j+1]){ 7. int temp = v[j]; 8. v[j] = v[j+1]; 9. v[j+1] = temp; 10. } 11.} SHELLSORT O mtodo shellsort permite trocas de registros que esto distantes um do outro. Os itens que esto separados h posies so rearranjados de tal forma que todo h-simo - item leva a uma sequncia ordenada. Tal sequncia dita h-ordenada. Na primeira passada (h=4), O e N (posies 1e 5) so comparados e trocados; A seguir, R e A (posies 2 e 6) so comparados e trocados. Na segunda passada (h=2), N, D e O, nas posies 1, 3 e 5, so rearranjados para resultar em D, N e O nessas posies; Da mesma forma A, E e R, nas posies 2, 4 e 6, so comparados e mantidos nos seus 19 lugares. A ltima passada (h=1) corresponde ao algoritmo de insero; entretanto, nenhum item precisa se mover para as posies muito distantes. QUICKSORT Algoritmo de ordenao interna mais rpido que se conhece para uma ampla variedade 20 de situaes, sendo provavelmente mais utilizado do que qualquer outro algoritmo. Idia Bsica Dividir o problema de ordenar um conjunto com n itens em dois problemas menores. Os problemas menores so ordenados independentemente e depois os resultados so combinados para produzir a soluo do problema maior. Enquanto o algoritmo de ordenao bolha coloca em sua posio (no final do vetor) o maior elemento, a ordenao rpida faz isso com um elemento arbitrrio x, chamado de piv. Podemos escolher como piv o primeiro elemento do vetor e posicionar esse elemento em sua posio correta em uma primeira passada. Suponha que esse elemento x, deva ocupar a posio i do vetor, de acordo com a ordenao, ou seja, que essa seja a sua posio definitiva. Sem ordenar o vetor completamente, esse fato pode ser reconhecido quando todos os elementos v[0], ..., v[i-1] so menores do que x, e todos os elementos v[i+1], ...,v[n-1] so maiores do que x. Caso se suponha que x j esteja na sua posio correta, com ndice i, h dois problemas menores para serem resolvidos: Ordenar os (sub) vetores formados por v[0], ..., v[i-1] e por v[i+1], ...,v[n-1]. Esses subproblemas so resolvidos de forma semelhante, cada vez com vetores menores, e o processo continua at os vetores que devem ser ordenados terem zero ou um elemento, caso no qual sua ordenao j est concluda. A implementao do quicksort normalmente recursiva, para facilitar a ordenao dos dois vetores menores encontrados.

EXEMPLO 1. void rapida(int n, int* v) 2. { 3. if(n<=1) 4. return; 5. else{ 28 6. int x = v[0]; 7. int a = 1; 8. int b = n-1; 9. do{ 10. while((a<n)&&(v[a]<=x)) 11. a++; 12. while(v[b]>x) 13. b--; 14. if(a<b){ 15. int temp = v[a]; 16. v[a] = v[b]; 17. v[b] = temp; 18. a++; 19. b--; 20. } 29 21. }while(a<=b); 22. v[0] = v[b]; 23. v[b] = x; 24. 25. rapida(b, v); 26. rapida(n-a, &v[a]); 27. } 28. } HEAPSORT Mtodo de ordenao cujo princpio de funcionamento utiliza o mesmo princpio da ordenao por seleo. Selecione o menor item do vetor e a seguir troque-o com o item que est na primeira posio do vetor. Repita estas duas 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 custa n-1 comparaes. MTODOS DE ORDENAO: DISTRIBUIO. Os mtodos de ordenao por distribuio de chave efetivam a ordenao pela execuo de sucessivos passos sobre um vetor em C. Tomando em cada um deles apenas uma parte da chave, a qual dividida em tantas partes como sero os passos. A cada passo, o vetor ordenado com base naquela frao da chave que corresponde ao passo. Tomemos como exemplo um vetor C, no qual todos os valores inteiros esto contidos no intervalo [0...999]. Podemos dividir cada elemento em trs partes de um dgito decimal e classificar o vetor em trs passos. Devemos tomar em primeiro lugar o dgito das unidades, depois o das dezenas e, por ltimo, o das centenas, como segue:

327 981 422 475 549 632 787 849 347 725 672 981 422 632 672 475 725 327 787 347 549 849 422 725 327 632 347 549 849 672 475 981 787 327 347 422 475 549 632 672 725 787 849 981 Est distribuio implica em vrias ordenaes do vetor enquanto os demais mtodos fazem uma ordenao. Para realmente analisarmos a eficincia do mtodo temos que considerar o problema de cada um dos passos. A cada passo considerado apenas um dgito decimal para cada elemento, o que significa que temos apenas dez valores possveis para cada elemento (0 a 9). No caso, em que temos poucos valores diferentes podemos fazer a ordenao de uma maneira muito eficiente: 1 - Fazemos uma distribuio por freqncia dos valores das chaves; isto , determinamos o nmero de elementos para cada dgito: Dgito: 0123456789d Frequncia: 0 0 3 0 0 2 0 3 0 2 f 2 - Representamos para cada dgito a frequncia acumulada at o dgito anterior: Dgito: 0123456789d Freq. Acum.: 0 0 1 4 4 4 6 6 9 9 f 3 - Com o uso da tabela de freqncias acumuladas, movemos os valores do vetor C para um vetor auxiliar C1, de tal forma que os elementos de C com dgitos iguais a di sero movidos para o vetor C1, a partir da posio f(di)+1. Assim, os elementos de C com dgito igual a 5 sero movidos para o vetor C1 nas posies f(5) + 1 e f(5) + 2, isto , para as posies 5 e 6 de C1. O nmero de partes que dividimos as chaves arbitrrio, no entanto, se cada parte apresentar um grande nmero de valores possveis, teremos as tabelas de freqncias igualmente grandes. muito importante observar que este mtodo apresenta um desempenho excelente quando desejamos ordenar um vetor contendo valores situados em um intervalo pequeno. Se desejarmos ordenar um vetor contendo valores em um intervalo de [0...99], e usarmos uma tabela de frequncia com 100 elementos podemos ordenar o vetor em um nico passo.

MTODOS DE ORDENAO: INSERO E INTERCALAO. Ordenao por Insero Em cada passo, a partir de i=2, o i-simo item da sequncia fonte apanhado e transferido para a sequncia destino, sendo inserido no seu lugar apropriado. EXEMPLO 1. void Insercao(TipoItem *A, TipoIndice n) 2. { 3. TipoIndice i, j; 4. TipoItem x; 5. for(i = 2; i<=n; i++) 6. { 7. x=A[i]; 5 8. j=i-1; 9. A[0]=x;//sentinela

10. while(x.Chave<A[j].Chave) 11. { 12. A[j+1]=A[j]; 13. j--; 14. } 15. A[j+1]=x; 16. } 17. } A colocao do item no seu lugar apropriado na sequncia destino realizada movendo-se itens com chaves maiores para a direita e se ento inserindo o item na posio deixada vazia. Nesse processo de alternar comparaes e movimentos de registros existem duas condies distintas que podem causar a terminao do processo: 1. Um item com chave menor que o item em considerao encontrado; 2. O final da sequncia destino atingido esquerda. A melhor soluo para a situao de um anel com duas condies de terminao a utilizao de um registro sentinela. Na posio zero do vetor colocamos o prprio registro em considerao. Para tal, o ndice do vetor tem de ser estendido para 0...n. O nmero mnimo de comparaes e movimentos ocorre quando os itens esto originalmente em ordem, e o nmero mximo ocorre quando os itens esto originalmente na ordem reversa, o que indica um comportamento natural para o algoritmo. O mtodo de insero o mtodo a ser utilizado quando o arquivo est quase ordenado. tambm um bom mtodo quando se deseja adicionar poucos itens a um arquivo j ordenado e depois obter outro arquivo ordenado. Esse mtodo to simples quanto o algoritmo de ordenao por seleo. um mtodo estvel, pois ele deixa os registros com chaves iguais na mesma posio relativa. Ordenao por Intercalao Esse mtodo de ordenao normalmente no independente, ele envolve o uso de um dos outros mtodos de ordenao. Ele consiste na obteno de um segmento ordenado Z a partir de dois segmentos X e Y tambm ordenados, por intercalao. X: 15 17 19 28 37 45 Y: 12 13 25 29 40 50 Z: 12 13 15 17 19 25 28 29 37 40 45 50 A intercalao feita uma varredura simultnea dos segmentos X e Y sendo a cada vez movido o menor dos dois elementos para Z. A forma pela qual so obtidos os segmentos classificados do vetor C que diferencia os mtodos de classificao por intercalao. O mais comum dividir-se o vetor C em um nmero fixo de segmentos e aplicar-se a cada segmento um dos mtodos de ordenao (Heapsort, Quicksort, Bubblesort, etc.) EXEMPLO 1. void intercala(int vet[], int esq, int dir) 2. { 3. int meio, Temp, i, j, k, t, m; 4. int temp[10]; 5. if(dir - esq == 1) 6. { 7. if(vet[esq] > vet[dir])

8. { 9. Temp = vet[esq]; 10. vet[esq]=vet[dir]; 11. vet[dir]=Temp; 12. } 13. } 14. else 15. { 16. if (esq != dir) 17. { 18. meio = (esq+dir)/2; 19. intercala(vet, esq, meio-1); 20. intercala(vet, meio, dir); 21. i = esq; 22. j = meio; 23. k = -1; 24. while( (i <= meio-1) && (j <= dir) ) 25. { 26. k = k+1; 27. if(vet[i] <= vet[j]) 28. { 29. temp[k] = vet[i]; 30. i++; 31. } 32. else 33. { 34. temp[k] = vet[j]; 35. j++; 36. } 37. } 38. if(j > dir) 39. { 40. for (t=0; t<meio-i; t++) 41. vet[dir-t] = vet[meio-1-t]; 42. } 43. for (t=0; t<=k; t++) 44. vet[esq+t] = temp[t]; 45. } 46. } 47. } A intercalao utilizada como uma operao auxiliar na ordenao.