Upload
vuminh
View
222
Download
0
Embed Size (px)
Citation preview
Programação II
Busca em Vetor(search)
Bruno FeijóDept. de Informática, PUC-Rio
Busca em Vetor
• Problema:– Entrada:
• vetor v com n elementos• elemento d a procurar
– Saída• m se o elemento procurado está em v[m]• -1 se o elemento procurado não está no vetor
• Tipos de Busca em Vetor– Linear (ou sequencial)– Binária
Busca Linear
Busca Linear em Vetor
int busca(n,vetor v de inteiros,elemento)i = 0para cada i, enquanto i<n
se elemento é igual a v[i]retorna i
retorna -1
int busca(n,int * v, int d){
int i;for (i=0; i<n; i++)
if (d == v[i])return i;
return -1;}
Busca Binária
A Ideia da Busca Binária
• Vetor deve estar inicialmente ordenado• Idéia:
– Como procurar uma palavra no dicionário sucessivamente abrindo pelo meio a parte que deve conter a palavra
• Procedimento:– compare o elemento d com o elemento do meio de v– se o elemento d for menor, pesquise a primeira metade do vetor– se o elemento d for maior, pesquise a segunda parte do vetor– se o elemento d for igual, retorne a posição– continue o procedimento subdividindo a parte de interesse até
encontrar o elemento d ou chegar ao fim
Algoritmo de Busca Binária
int buscaInt(n,vetor v de inteiros, c) /* c: o elemento procurado */ini = 0fim = n-1enquanto ini ≤ fim
meio = (ini + fim)/2compara c com informaçao em v[meio]
se < então fim = meio-1se > então ini = meio+1se = então retorna meio
retorna -1 para indicar insucesso
Caso de comparar dois inteiros:
static int compInt(int c, int b){
if (c < b)return -1;
else if (c > b)return 1;
elsereturn 0;
}
ini
fim
meio
0
n-1
i
- A função de comparação deve retornar negativo (e.g. -1) se for menor, positivo (e.g. 1) se for maior e zero se for igual.
- Geralmente, c e v[meio] não são do mesmo tipo (e.g. se v é um vetor de ponteiros para uma estrutura, estaremos buscando um de seus componentes).
- Este algoritmo é geral. Só muda o que está em vermelho e itálico. Note que antes de retornar meio, pode haver necessidade de processamento extra, e.g. quando há conjunto de valores iguais e queremos o primeiro:
...{adao,25}{ana, 19} ←{ana, 20}
meio→ {ana, 21}{ana, 25}...
queremos a anamais jovem
Busca Binária em Vetor de Inteiros
int buscaInt(n,vetor v de inteiros, c inteiro) /* c: o elemento procurado */ini = 0fim = n-1enquanto ini ≤ fim
meio = (ini + fim)/2compara c com v[meio]
se < então fim = meio-1se > então ini = meio+1se = então retorna meio
retorna -1 para indicar insucesso
static int compInt(int c, int b){
if (c < b)return -1;
else if (c > b)return 1;
elsereturn 0;
}
int buscaInt(int n, int * v, int c){
int ini=0;int fim=n-1;int meio, cmp;while (ini <= fim){
meio=(ini+fim)/2;cmp = compInt(c,v[meio]);if (cmp < 0)
fim=meio-1;else
if (cmp > 0)ini=meio+1;
elsereturn meio;
}return -1;
}
ini
fim
meio
0
n-1
i
Responda: qual é o valor de ini quando o valor procurado não é encontrado? Há duas situações!
Versão com switch
int buscaInt(int n, int * v, int d){
int ini=0;int fim=n-1;int meio;while (ini <= fim){
meio=(ini+fim)/2;switch (compInt(d,v[meio])){
case -1:fim=meio-1;break;
case 1:ini=meio+1;break;
case 0:return meio;
}}return -1;
}
int compInt(int a, int b){
if (a < b)return -1;
else if (a > b)return 1;
elsereturn 0;
}
Exercício Busca String
Escreva um programa completo que busca um nome em um vetor ordenadoalfabeticamente.Dicas: o que muda está em vermelho e itálico no slide 8. Use strcmp da biblioteca de string.
Atenção: a função strcmp(a,b) já faz o trabalho de retornar -1 se a <b, +1 se a>b e 0 se a=b.Entretanto, por razões didáticas (e de treino para a prova), a recomendação é criar uma funçãochamada compString para substituir a compInt do código do slide “Busca Binária em Vetor de Inteiros”(mesmo que essa compString seja apenas o uso direto da strcmp).
Exercício com Estrutura
Escreva um programa completo que monta um vetor de ponteiros para estrutura Pessoa (ordenado crescentemente por nome e idade) e busca um nome nesse vetor (sem se preocupar com a idade).
struct pessoa{
char * nome;int idade;
};typedef struct pessoa Pessoa;
Solução do Exercício com Estrutura
...int main(void){
...i = buscaPes(N, tabPessoa, "daniela");...
}
#include <string.h>#include "pessoa.h"
static int compPessoa(char * a, Pessoa * b){
return strcmp(a,b->nome);}
int buscaPes(int n, Pessoa ** v, char * nome){
int ini=0;int fim=n-1;int meio, cmp;while (ini <= fim){
meio=(ini+fim)/2;cmp=compPessoa(nome,v[meio]);if (cmp<0)
fim=meio-1;else if (cmp>0)
ini=meio+1;else
return meio;}return -1;
}
Variações
• O vetor tem um critério complexo de ordenação (i.e. um critério primário e vários secundários de desempate)
– Por exemplo, um campeonato que pode estar ordenado com o critério primário de Pontos Ganhos (PG) e desempate por Saldo de Gols (SG) e Gols Pró (GP).
– Neste caso, a função de comparação deve ter expressões booleanas que representem o critério. Por exemplo:
• Tipo de informação retornada– O índice do elemento encontrado ou -1– O endereco do elemento encontrado (i.e. o ponteiro)– O valor de um campo específico ou outra informação qualquer– Verdade (e.g. 1) se encontrou, ou Falso (0), caso contrário
• Tratamento de repetições
static int compTime(int pg, int sg, int gp, Campeonato * time){
if (pg < time->pg || (pg == time->pg && sg < time->sg) || ...)return -1;
else if (pg > time->pg || (pg == time->pg && pg > time-> sg) || ...)return 1;
elsereturn 0;
}
Exercício com Estrutura e Critério Secundário
Escreva um programa completo, que monta um vetor de ponteiros paraestrutura Pessoa (ordenado crescentemente por nome e idade) e buscaum nome com uma determinada idade nesse vetor.
struct pessoa{
char nome[81];int idade;
};typedef struct pessoa Pessoa;
int buscaPessoa(int n, Pessoa ** v,char * nome)
{int ini=0;int fim=n-1;int meio, cmp;while (ini <= fim){
meio=(ini+fim)/2;cmp=compPessoa(nome,v[meio]);if (cmp<0)
fim=meio-1;else if (cmp>0)
ini=meio+1;else
return meio;}return -1;
} int main(void){
Pessoa * tab[N];Pessoa * p;...p = buscaPessoa(N,tab,nome);if (p==NULL)
printf("elemento nao existe\n");idade = p->idade;...
}
Busca que Retorna o Endereço do ElementoPessoa * buscaPessoa(int n, Pessoa ** v,char * nome){
int ini=0;int fim=n-1;int meio, cmp;while (ini <= fim){
meio=(ini+fim)/2;cmp=compPessoa(nome,v[meio]);if (cmp<0)
fim=meio-1;else if (cmp>0)
ini=meio+1;else
return v[meio];}return NULL;
}
Busca que Trata de Repetições
• Considere um tipo que representa as licenças dos funcionário de uma empresa, definido pela estrutura a seguir:
struct licenca {char nome[51]; /* nome do funcionario */Data inicio; /* data de inicio da licenca */Data final; /* data de final da licenca */
};
typedef struct licenca Licenca;
• Os campos inicio e final são do tipo Data, descrito a seguir:struct data {
int dia, mes, ano;};typedef struct data Data;
• Escreva uma função que faça uma busca binária em um vetor de ponteiros para o tipo Licenca, cujos elementos estão em ordem cronológica, de acordo com a data de início das licenças, com desempate pela ordem alfabética de acordo com o nome dos funcionários. Se existir mais de uma licença com início na data procurada, a função deve retornar o índice da primeira delas. Se não houver uma licença com a data procurada, a função deve retornar -1. Sua função deve ter o seguinte cabeçalho:
int busca (Licenca ** v, int n, Data d);
Busca que Trata de Repetições
int busca(Licenca ** v, int n, Data d){int ini=0, fim=n-1, meio, cmp;while (ini <= fim){ meio = (ini+fim)/2;cmp = compData(d,v[meio]); // usa v[meio]->inicioif (cmp < 0) // -1fim = meio–1;
else if (cmp > 0) // 1ini = meio+1;
else{
while(meio>0 && compData(d,v[meio-1])==0) meio--; // procura primeira ocorrencia, caso contrário e’ aleatorio
return meio;}
} return -1; /* não encontrou */
}
escreva compData usando ano, mes e dia
TÓPICOS AVANÇADOS
Binary Search da stdlib do C
void * bsearch(void * info, void * v, int n, int tam,int (*cmp)(const void *, const void *));
info: ponteiro para a informação que se deseja buscarv: vetor de ponteiros genéricos (ordenado)n: número de elementos do vetortam: tamanho em bytes de cada elemento (use sizeof para especificar)cmp: ponteiro para função que compara elementos genéricos, sendo o primeiro
o endereço da informação e o segundo é o ponteiro para um dos elementosdo vetor. O critério de comparação deve ser o mesmo da ordenação de vint nome(const void * a, const void * b);deve retornar <0 se a<b, >0 se a>b e 0 se a == b
chamada: int d = 23;int * p;p = (int *)bsearch(&d,v,N,sizeof(int),compInt); // N é o tamanho de vi = p-v; // indice do elemento encontrado
const é para garantir quea função não modificaráos valores dos elementosstatic int compInt(const void * a, const void * b)
{int * info = (int *)a; // converte o ponteiro genericoint * bb = (int *)b; // converte o ponteiro genericoif (*info < *bb) // faz as comparacoes. Atenção para o *
return -1;else if (*info > *bb)
return 1;else
return 0;}
Análise de Complexidade
Busca Linear em Vetor
int busca(n,vetor v de inteiros,elemento)i = 0para cada i, enquanto i<n
se elemento é igual a v[i]retorna i
retorna -1
Pior Caso: o elemento não está no vetorNeste caso são necessárias n comparaçõesT(n) = n → O(n) Linear !
Melhor Caso: o elemento é o primeiro → O (1)
Caso Médio:n/2 comparações
T(n) = n/2 → O(n) Também Linear !
Se o vetor estivesse ordenado, poderíamos escrever uma função mais eficiente (onde retornaríamos -1 quando o elemento fosse menor do que v[i]), mas a complexidade continuaria O(n).Um algoritmo pode ter a mesma complexidade de um outro, porém pode ser mais, ou menos, eficiente. Eficiência e Complexidade são coisas diferentes !
int busca(n,int * v, int d){
int i;for (i=0; i<n; i++)
if (d == v[i])return i;
return -1;}
Na análise de complexidade, procuramos o “Pior Caso”, o “Melhor Caso” e o “Caso Médio”
Análise da Complexidade
• Pior Caso: elemento não está no vetor– cada repetição do loop gasta um tempo constante– a questão é, portanto: quantas vezes o loop será repetido?
• cada repetição do loop reduz o tamanho da busca pela metade:• repetição 1 → tamanho da busca é n /2• repetição 2 → tamanho da busca é n / 22
• repetição 3 → tamanho da busca é n /23
• ...• repetição i → tamanho da busca é n /2i
– as repetições param quando o tamanho da busca for 1, isto é n /2i = 1ou seja: 2i = n
– Portanto: i = log n, isto é: o loop é repetido log n vezes– Ou seja: T(n) = log n → O(log n)
• Melhor Caso: elemento está no meio → O(1)• Caso Médio (dedução mais complicada): O(log n)
Complexidades Comuns (notação Big O)
Notação Big OO(1) Tempo constanteO(log n) Tempo logarítmicoO(n) Tempo linearO(n log n) Tempo loglinear (ou quaselinear)O(n2) Tempo quadrático
Limite n2
nnnT21
21)( 2 −=
Quase linear
n
Diferença entre n e log n
36 seg100 anos94 608 000 000301 ano946 080 000211 mês2 592 000161 dia86 400121 h3 600910 min60061 min60
3 seg10 seg10O(log n)O(n)tamanho