Sunteți pe pagina 1din 73

Busca

Prof. Raimundo BARRETO


DCC/ICE/UFAM
Introduo
 Ser discutido diferentes estratgias para a busca de um
elemento num determinado conjunto de dados.
 A operao de busca encontrada com muita freqncia em
aplicaes computacionais, sendo portanto importante estudar
estratgias distintas para efetu-la.
 Por exemplo, um programa de controle de estoque pode buscar,
dado um cdigo numrico ou um nome, a descrio e as
caractersticas de um determinado produto.
 Se temos um grande nmero de produtos cadastrados, o mtodo
para efetuar a busca deve ser eficiente, caso contrrio a busca
pode ser muito demorada, inviabilizando sua utilizao.
Introduo
 Inicialmente, consideraremos que temos nossos dados
armazenados em um vetor e discutiremos os algoritmos
de busca que podemos empregar.
 A seguir, discutiremos a utilizao de rvores binrias de
busca, que so estruturas de rvores projetadas para
darem suporte a operaes de busca de forma eficiente.
 Finalmente, discutiremos as estruturas conhecidas como
tabelas de disperso (hash) que podem, como veremos,
realizar buscas de forma extremamente eficiente, fazendo
uso de espao de memria adicional.
BUSCA EM VETOR
Busca em Vetor

 Dado um vetor vet com n elementos, desejamos


saber se um determinado elemento elem est ou
no presente no vetor.
 Duas abordagens:
Busca Linear
Busca Binria
Busca Linear
 A forma mais simples de fazermos uma busca
num vetor consiste em percorrermos o vetor,
elemento a elemento, verificando se o elemento
de interesse igual a um dos elementos do vetor.
 Vamos imaginar que o vetor de nmeros
inteiros.
 A funo ter como valor de retorno o ndice do
vetor no qual foi encontrado o elemento; se o
elemento no for encontrado, o valor de retorno
deve ser igual a 1.
Busca Linear

int busca (int n, int* vet, int elem)


{
int i;
for (i=0; i<n; i++)
if (elem == vet[i])
return i; /* elemento encontrado */
/* percorreu todo o vetor e no encontrou elemento */
return -1;
}

E se os elementos estiverem em um vetor ordenado,


h alguma soluo mais eficiente?
Busca Linear

int busca (int n, int* vet, int elem)


{
int i;
for (i=0; i<n; i++)
if (elem == vet[i])
return i; /* elemento encontrado */
else if (vet[i] > elem)
return -1; /* interrompe a busca */
/* percorreu todo o vetor e no encontrou elemento */
return -1;
}

Essa soluo s mais eficiente se o elemento no estiver


no vetor, h alguma outra soluo mais eficiente?
Busca Binria
 A idia do algoritmo testar o elemento que buscamos
com o valor do elemento armazenado no meio do vetor.
 Se o elemento que buscamos for
menor que o elemento do meio, sabemos que, se o elemento
estiver presente no vetor, ele estar na primeira parte do vetor;
maior que o elemento do meio, estar na segunda parte do vetor;
igual, achamos o elemento no vetor.
 Se concluirmos que o elemento est antes ou depois do
elemento do meio, repetimos o procedimento
considerando apenas a parte que restou.
 Este procedimento continuamente repetido,
subdividindo a parte de interesse, at encontrarmos o
elemento ou chegarmos a uma parte do vetor com
tamanho zero.
Exemplo de Cdigo
int busca_bin (int n, int* vet, int elem)
{
/* no inicio consideramos todo o vetor */
int ini = 0;
int fim = n-1;
int meio;
/* enquanto a parte restante for maior que zero */
while (ini <= fim) {
meio = (ini + fim) / 2;
if (elem < vet[meio])
fim = meio 1; /* ajusta posico final */
else if (elem > vet[meio])
ini = meio + 1; /* ajusta posico inicial */
else
return meio; /* elemento encontrado */
}
/* no encontrou: restou parte de tamanho zero */
return -1;
}
Pergunta

Quantas vezes precisamos repetir o


procedimento de subdiviso para
concluirmos que o elemento no est
presente no vetor?

Lembre que, a cada


repetio, a parte considerada na busca
dividida pela metade.
Resposta

Repetio Tamanho

1 n

2 n/2

3 n/4

... ...
log2 n 1
Busca Recursiva

int busca_bin_rec (int n, int* vet, int elem)


{
/* testa condio de parada */
if (n <= 0) return 0;
else {
/* buscar o elemento entre os ndices 0 e n-1 */
int meio = (n - 1) / 2;
if (elem < vet[meio])
return busca_bin_rec(meio,vet,elem);
else if (elem > vet[meio])
return busca_bin_rec(n1-meio,&vet[meio+1],elem);
else
return 1; /* elemento encontrado */
}
}
RVORE BINRIA DE BUSCA
Introduo
 O algoritmo de busca binria em vetores tem bom desempenho e
deve ser usado quando temos os dados j ordenados.
 No entanto, se precisarmos inserir e remover elementos da
estrutura e, ao mesmo tempo, dar suporte a funes de busca
eficientes, a estrutura de vetor no se torna adequada.
Para inserirmos um novo elemento num vetor ordenado, temos que
rearrumar os elementos no vetor, para abrir espao para a insero
do novo elemento.
Situao anloga ocorre quando removemos um elemento do
vetor.
 Precisamos portanto de uma estrutura dinmica que d suporte a
operaes de busca.
Introduo
 As rvores binrias que sero consideradas nesta
seo tm uma propriedade fundamental:
o valor associado raiz sempre maior que o valor
associado a qualquer n da sub-rvore esquerda (sae),
e sempre menor que o valor associado a qualquer n da
sub-rvore direita (sad).
 Essa propriedade garante que, quando a rvore
percorrida em ordem infixada (sae - raiz - sad), os
valores so encontrados em ordem crescente.
Introduo
Introduo
 Para procurar um valor numa rvore, comparamos o
valor que buscamos com o valor associado raiz.
 Em caso de igualdade
o valor foi encontrado;
 Se o valor procurado for menor que o valor da raiz
a busca continua na sae;
 Se o valor procurado for maior que o valor da raiz
a busca continua na sad.
 Por essa razo, estas rvores so freqentemente
chamadas de rvores binrias de busca.
Introduo
 Naturalmente, a ordem infixada dependente da
aplicao.
 Se a informao a ser armazenada em cada n da
rvore for um nmero inteiro podemos usar os
operadores relacionais (<, >, =).
 Porm, se tivermos que considerar casos em que a
informao mais complexa, a funo de
comparao deve ser definida pelo programador,
especificamente para cada caso.
Tipo da rvore Binria

struct arv {
int info;
struct arv* esq;
struct arv* dir;
};

typedef struct arv Arv;


Operao de Busca

Arv* busca (Arv* r, int v)


{
if (r == NULL) return NULL;
else if (r->info > v) return busca(r->esq,v);
else if (r->info < v) return busca(r->dir,v);
else return r;
}
Operao de Insero
Arv* insere (Arv* a, int v)
{
if (a==NULL) {
a = (Arv*)malloc(sizeof(Arv));
a->info = v;
a->esq = a->dir = NULL;
}
else if (v < a->info)
a->esq = insere(a->esq,v);
else /* v < a->info */
a->dir = insere(a->dir,v);
return a;
}
Operao de Remoo

 Essa operao um pouco mais complexa que a


de insero. Existem trs situaes possveis.
 A primeira quando se deseja retirar um
elemento que folha da rvore.
 A segunda situao acontece quando o n a ser
retirado possui um nico filho.
 A terceira situao ocorre quando o n a ser
retirado tem dois filhos.
Operao de Remoo
Primeira e Segunda Situaes
Operao de Remoo
Primeira e Segunda Situaes

 A primeira, e mais simples, quando se deseja


retirar um elemento que folha da rvore. Neste
caso, basta retirar o elemento da rvore e
atualizar o pai, pois seu filho no existe mais.
 A segunda situao, ainda simples, acontece
quando o n a ser retirado possui um nico filho.
Para retirar esse elemento necessrio antes
acertar o ponteiro do pai, pulando o n: o nico
neto passa a ser filho direto.
Operao de Remoo
Terceira Situao

Localiza o n a ser eliminado (n 6)


Encontra o n mais a direita da sub-rvore da esquerda (n 4)
Trocar os valores entre o n a ser eliminado e o n encontrado.
Localiza novamente o n a ser eliminado (n 6)
Operao de Remoo
Terceira Situao

 Para poder retirar esse n da rvore, devemos


proceder da seguinte forma:
encontramos o elemento que precede o elemento a ser
retirado na ordenao. Isto equivale a encontrar o
elemento mais direita da sub-rvore esquerda;
trocamos a informao do n a ser retirado com a
informao do n encontrado;
retiramos novamente o n encontrado (que agora
contm a informao do n que se deseja retirar).
Observa-se que retirar tal n agora trivial, pois esse
um n folha ou um n com um nico filho.
Operao de Remoo

Arv* retira (Arv* r, int v)


{
if (r == NULL)
return NULL;
else if (r->info > v)
r->esq = retira(r->esq, v);
else if (r->info < v)
r->dir = retira(r->dir, v);
else { /* achou o elemento */
...
Operao de Remoo
...
else { /* achou o elemento */
/* elemento sem filhos */
if (r->esq == NULL && r->dir == NULL) {
free (r);
r = NULL;
}
/* s tem filho direita */
else if (r->esq == NULL) {
Arv* t = r;
r = r->dir;
free (t);
}
/* s tem filho esquerda */
else if (r->dir == NULL) {
Arv* t = r;
r = r->esq;
free (t);
}
...
Operao de Remoo
...
/* tem os dois filhos */
else {
Arv* pai = r;
Arv* f = r->esq;
while (f->dir != NULL) {
pai = f;
f = f->dir;
}
/* troca as informaes */
r->info = f->info;
f->info = v;
r->esq = retira(r->esq,v);
}
}
return r;
}
TABELAS DE DISPERSO
(HASH)
Introduo
 Armazenar dados de alunos em uma disciplina
 Informaes dos alunos: matrcula, nome, email e turma
 Cada aluno identificado pela matrcula (7dgs)
 Permitir o acesso a qualquer aluno em ordem constante
muito caro
7 dgitos = 10 milhes de elementos
Cada aluno ocupa 100 bytes
Total: 1 trilho de bytes (1GB)
 Como temos 50 alunos o total deveria ser 5000 bytes
(5KB)
Tipo de Dado

struct aluno {
int mat;
char nome[81];
char email[41];
char turma;
};

typedef struct aluno Aluno;


Introduo

A forma de resolver o problema de gasto


excessivo de memria, mas ainda
garantindo um acesso rpido, atravs
do uso de tabelas de disperso
(hash table)
Conceitos

Esquema da Tabela Hash

K3
K4
K5 Funo Hash
K1
K2

Tabela Hash
Conceitos

h(K4)

Conjunto de Chaves h(K3)


Busca Possveis
h(K5)
K4 K3
K5
K1
K2

h(K1)

h(K2)

Tabela Hash
Conceitos
Hash em Banco de Dados Carla
Mariana Bloco 1

Adriana 19/04/85
Paulo 03/12/87
Carla 13/08/81 Jos
Funo Hash
Jos 21/02/85 Juliana Bloco 2

Juliana 09/10/85 Adriana


Mariana 01/07/81
Paulo
Chaves (Dados)
Bloco 3
Funes Hash

 A funo de disperso (funo hash) mapeia uma


chave de busca num ndice da tabela.
 Tipos de Funes Hash
Mtodo da Diviso
Mtodo da Multiplicao
Propriedades

Propriedades de Boas Funes Hash

Ser de eficientemente avaliada (fcil implementao).

Devem espalhar bem as chaves de busca (evitar colises).


Mtodo da Diviso

h (k) = k resto m

Potncias de 2 deve ser evitada para valores de m.

m deve ser um nmero primo distante de pequenas


potncias de 2 (m grande).

Tambm conhecido como Mtodo da Congruncia


Linear.
Mtodo da Diviso

Exemplo :

k = 10028
m = 5013 (m o tamanho da tabela)

h (10028) = 10028 resto 5013


h (10028) = 2
Mtodo da Diviso

Exemplo de Funo Hash

int hash (int mat)


{
return (mat%N);
}
Mtodo da Multiplicao

h(k) = (m * (kA resto 1))

m normalmente uma potncia de 2.


A uma constante : 0<A<1
Extrair a parte fracionria de kA, ou seja , kA resto 1.
Utilizar o piso(floor) do resultado
A = 0.6180339887...
Tambm conhecido como Mtodo da Congruncia
Linear Multiplicativo.
Mtodo da Multiplicao

Exemplo :
k = 123456 | m = 1024 | A = 0.61803...
h(k) = 1024*(123456*0.61803... resto 1)
= 1024*(76300,0041151... resto 1)
= 1024*0,0041151...
= 4.21386...
h(k)= 4
Funes de Converso

Converso de Strings para Inteiros

Mtodo da Adio;

Mtodo dos 3 primeiros caracteres;


Mtodo da Adio

Os valores dos caracteres da string


correspondentes na tabela ASCII so
somados.

A chave o resultado dessa soma.


Mtodo da Adio

Exemplo : String = aed2

m = 256 ( tamanho da tabela)

aed2 = 97 + 101 + 100 + 50 (valores dos


caracteres na tabela ASCII)

h (348) = 348 resto 256 = 92


Mtodo dos 3 primeiros
caracteres

h(k)= (chave[0]*B0 + chave[1]*B1 + chave[2]*B2) resto m

m o tamanho da tabela

chave[ ] so os caracteres da string

B um base qualquer
Mtodo dos 3 primeiros
caracteres

Exemplo : String = Joseph

M=10007 ( tamanho da tabela)


B=53

h(Joseph) = (74*1 +111* 53 +115 *2809) resto M


= 8768
Colises

Coliso o mapeamento de duas (ou mais) chaves


para um mesmo posio no vetor.

11 Coliso 1

Mtodos para o tratamento de colises:

Encadeamento Direto (chaining);

Endereamento aberto (open adressing).


Colises

Encadeamento Direto (chaining)


Armazena em listas encadeadas as chaves que
sofreram coliso.

10 20 30

4 14

6 12 25
Tabela Hash
Colises

Encadeamento Direto (Chaining)


Vantagens:
Nmero ilimitado de elementos e colises;
No necessrio prever antecipadamente a quantidade
de elementos a serem inseridos.
Desvantagens:

Devido ao nmero ilimitado de elementos que podem ser


inseridos, o desempenho da tabela hash fica prejudicado.
Colises

Endereamento Aberto (open addressing)


Neste mtodo as chaves que sofreram coliso so
inseridas na prpria tabela.

Algumas Tcnicas

Linear Probing
Quadratic Probing
Double Hashing
rea de Overfow
Colises

Linear Probing
O valor do ndice correspondente a chave que sofreu
coliso incrementado at que uma posio disponvel
seja encontrada, nova_posio=h(x)+i.

11 Coliso 1 1
h(x)+i
Probing
11

5 5
Tabela hash Tabela aps a aplicao
de linear probing
Colises
Linear Probing
Vantagens
Mtodo simples e fcil de implementar, que pode gerar
bons resultados.
Desvantagens
Faz surgir o fenmeno Clustering.
21Coliso 1 1 1 1
Probing 11 21 Coliso 11 11 11
h(x)+1
3 Probing 3 21 Coliso 3 3
h(x)+2
Probing 21
5 5 h(x)+3
5 5
Colises

Quadratic Probing
Comportamento parecido com Linear Probing, s que
agora, o incremento quadrtico, nova_posio= h(x) + i2.

21 Coliso 1 1 1
Probing
h(x) + 1 2 21 Coliso 2 2
i=1;
Probing
h(x) + 4
i=2;

21
Tabela Hash Tabela Hash Tabela aps a aplicao
de quadratic probing
Colises

Quadratic Probing

Vantagens

uma alternativa ao linear probing, pois evita o


clustering primrio.

Desvantagens
Poder surgir o clustering secundrio, no entanto,
este menos severo que o primrio.
Colises
Double Hashing
Utiliza duas funes hashing para tratar a coliso, a primeira
a mesma utilizada para o mapeamento da chave e a segunda
uma espcie de funo incremento, nova_posio= h(x)+ig(x).
x=21;
m=5;
0 0
h(x)= x mod m;
g(x)= 2+(x mod (m - 2)); 21 Coliso 1 1 1 1
nova_posio=h(x) + g(x).
Probing 2 2
g(x)+h(x)
Calculando:
3 3 21
h(x)= 21 mod 5 = 1;
g(x)= 2+(21 mod (5-2)) = 2; 4 4
nova_posio=1 + 2 = 3. Tabela Hash Tabela aps a aplicao
de Double Hashing
Colises

Double Hashing
Vantagens:
uma tcnica eficiente, que nos permite a livre escolha
da funo incremento.

Desvantagens:
Maior nvel de complexidade;
Se a funo incremento for mal escolhida, ela nunca
percorrer todos os espaos da tabela.
Colises

rea de Overflow
A tabela Hash dividida em rea Primria erea
Secundria, est ltima armazena as colises.
21Coliso 1 1
2 2
rea primria Probing 3 3
ult +1 4
rea secundria 4
ult=0; 21 ult=1;
ult = a posio do ltimo
elemento na rea
secundria.
Tabela Hash Tabela aps o uso
da rea de overflow
Colises

rea de Overflow

Vantagens
Colises no usam o espao da rea primria;
Acesso mais rpido.

Desvantagens
Dois parmetros afetam diretamente a performance
da tabela, o tamanho das reas primria e secundria.
Aplicaes

 Em Banco de Dados;
 Em implementaes das tabelas de smbolos dos
compiladores das linguagens de programao;
 Em Jogos, para acessar rapidamente a posio
para qual o personagem ir mover-se;
 Na implementao de um dicionrio.
Exemplo
Tipos de Dados

struct aluno {
int mat;
char nome[81];
char email[41];
char turma;
};

typedef struct aluno Aluno;

#define N 100
typedef Aluno* Hash[N];
Exemplo
Funo Hash

int hash (int mat)


{
return (mat%N);
}
Exemplo
Busca

Aluno* busca (Hash tab, int mat)


{
int h = hash(mat);
while (tab[h] != NULL) {
if (tab[h]->mat == mat)
return tab[h];
h = (h+1)%N;
}
return NULL;
}
Exemplo
Insero

Aluno* insere (Hash tab, int mat, char* nome,


char* email, char turma)
{
int h = hash(mat);
while (tab[h] != NULL) {
if (tab[h]->mat == mat)
break;
h = (h+1) % N;
}
if (tab[h]==NULL) { /* no encontrou o elemento */
tab[h] = (Aluno*) malloc(sizeof(Aluno));
tab[h]->mat = mat;
...
Exemplo

...
/* atribui informao */
strcpy(tab[h]->nome,nome);
strcpy(tab[h]->email,email);
tab[h]->turma = turma;
return tab[h];
}
else printf(Aluno ja cadastrado\n);
}
Exemplo
Busca - 2 Funo Hash

h(x) = (N 2 x) % (N 2)

int hash2 (int mat)


{
return N - 2 - mat%(N-2);
}
Exemplo
Busca - 2 Funo Hash
Aluno* busca (Hash tab, int mat)
{
int h = hash(mat);
int h2 = hash2(mat);
while (tab[h] != NULL) {
if (tab[h]->mat == mat)
return tab[h];
h = (h+h2) % N;
}
return NULL;
}
Exemplo
Coliso com Lista Encadeada

struct aluno {
int mat;
char nome[81];
char turma;
char email[41];
struct aluno* prox;
};
typedef struct aluno Aluno;
Exemplo
Busca - Coliso com Lista Encadeada

Aluno* busca (Hash tab, int mat)


{
int h = hash(mat);
Aluno* a = tab[h];
while (a != NULL) {
if (a->mat == mat)
return a;
a = a->prox;
}
return NULL;
}
Exemplo
Insero - Coliso com Lista Encadeada

Aluno* insere (Hash tab,int mat,char* nome, char turma)


{
int h = hash(mat);
Aluno* p = NULL; /* ponteiro para anterior */
Aluno* a = tab[h];
while (a != NULL) {
if (a->mat == mat)
break;
p = a;
a = a->prox;
}
...
Exemplo
Insero - Coliso com Lista Encadeada

...
if (a==NULL) { /* no encontrou o elemento */
a = (Aluno*) malloc(sizeof(Aluno));
a->mat = mat;
a->prox = NULL;
if (p==NULL)
tab[h] = a;
else
p->prox = a;
}
/* atribui informao */
strcpy(a->nome,nome);
a->turma = turma;
return a;
}

S-ar putea să vă placă și