You are on page 1of 73

Pesquisa em Memria Primria

ltima alterao: 7 de Setembro de 2010

Transparncias elaboradas por Fabiano C. Botelho, Israel Guerra e Nivio Ziviani


Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 1
Pesquisa em Memria Primria
Introduo - Conceitos Bsicos
Pesquisa Sequencial
Pesquisa Binria
rvores de Pesquisa
rvores Binrias de Pesquisa
sem Balanceamento
rvores Binrias de Pesquisa
com Balanceamento
rvores SBB
Transformaes para Manu-
teno da Propriedade SBB
Pesquisa Digital
Trie
Patricia
Transformao de Chave
(Hashing)
Funes de Transformao
Listas Encadeadas
Endereamento Aberto
Hashing Perfeito com or-
dem Preservada
Hashing Perfeito Usando
Espao Quase timo
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 2
Introduo - Conceitos Bsicos
Estudo de como recuperar informao a partir de uma grande massa
de informao previamente armazenada.
A informao dividida em registros.
Cada registro possui uma chave para ser usada na pesquisa.
Objetivo da pesquisa:
Encontrar uma ou mais ocorrncias de registros com chaves iguais
chave de pesquisa.
Pesquisa com sucesso X Pesquisa sem sucesso.
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 3
Introduo - Conceitos Bsicos
Conjunto de registros ou arquivos tabelas
Tabela:
associada a entidades de vida curta, criadas na memria interna
durante a execuo de um programa.
Arquivo:
geralmente associado a entidades de vida mais longa, armazenadas
em memria externa.
Distino no rgida:
tabela: arquivo de ndices
arquivo: tabela de valores de funes.
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 4
Escolha do Mtodo de Pesquisa mais Adequado a uma
Determinada Aplicao
Depende principalmente:
1. Quantidade dos dados envolvidos.
2. Arquivo estar sujeito a inseres e retiradas frequentes.
Se contedo do arquivo estvel importante minimizar o tempo
de pesquisa, sem preocupao com o tempo necessrio para
estruturar o arquivo
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 5
Algoritmos de Pesquisa Tipos Abstratos de Dados
importante considerar os algoritmos de pesquisa como tipos
abstratos de dados, com um conjunto de operaes associado a uma
estrutura de dados, de tal forma que haja uma independncia de
implementao para as operaes.
Operaes mais comuns:
1. Inicializar a estrutura de dados.
2. Pesquisar um ou mais registros com determinada chave.
3. Inserir um novo registro.
4. Retirar um registro especco.
5. Ordenar um arquivo para obter todos os registros em ordem de
acordo com a chave.
6. Ajuntar dois arquivos para formar um arquivo maior.
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 6
Dicionrio
Nome comumente utilizado para descrever uma estrutura de dados
para pesquisa.
Dicionrio um tipo abstrato de dados com as operaes:
1. Inicializa
2. Pesquisa
3. Insere
4. Retira
Analogia com um dicionrio da lngua portuguesa:
Chaves palavras
Registros entradas associadas com cada palavra:
pronncia
denio
sinnimos
outras informaes
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 5.1 7
Pesquisa Sequencial
Mtodo de pesquisa mais simples: a partir do primeiro registro,
pesquise sequencialmente at encontrar a chave procurada; ento
pare.
Armazenamento de um conjunto de registros por meio do tipo
estruturado arranjo:
#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;
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 5.1 8
Pesquisa Sequencial
void I ni ci al i za (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)
{ i f (T>n == MAXN)
pr i nt f ( "Erro : tabela cheia\n" ) ;
else { T>n++; T>Item[T>n] = Reg; }
}
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 5.1 9
Pesquisa Sequencial
Pesquisa retorna o ndice do registro que contm a chave x;
Caso no esteja presente, o valor retornado zero.
A implementao no suporta mais de um registro com uma mesma
chave.
Para aplicaes com esta caracterstica necessrio incluir um
argumento a mais na funo Pesquisa para conter o ndice a partir do
qual se quer pesquisar.
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 5.1 10
Pesquisa Sequencial
Utilizao de um registro sentinela na posio zero do array:
1. Garante que a pesquisa sempre termina:
se o ndice retornado por Pesquisa for zero, a pesquisa foi sem
sucesso.
2. No necessrio testar se i > 0, devido a isto:
o anel interno da funo Pesquisa extremamente simples: o
ndice i decrementado e a chave de pesquisa comparada
com a chave que est no registro.
isto faz com que esta tcnica seja conhecida como pesquisa
sequencial rpida.
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 5.1 11
Pesquisa Sequencial: Anlise
Pesquisa com sucesso:
melhor caso : C(n) = 1
pior caso : C(n) = n
caso medio : C(n) = (n + 1)/2
Pesquisa sem sucesso:
C

(n) = n + 1.
O algoritmo de pesquisa sequencial a melhor escolha para o
problema de pesquisa em tabelas com at 25 registros.
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 5.2 12
Pesquisa Binria
Pesquisa em tabela pode ser mais eciente Se registros forem
mantidos em ordem
Para saber se uma chave est presente na tabela
1. Compare a chave com o registro que est na posio do meio da
tabela.
2. Se a chave menor ento o registro procurado est na primeira
metade da tabela
3. Se a chave maior ento o registro procurado est na segunda
metade da tabela.
4. Repita o processo at que a chave seja encontrada, ou que
apenas um registro cuja chave diferente da procurada,
signicando uma pesquisa sem sucesso.
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 5.2 13
Algoritmo de Pesquisa Binria
TipoIndice Binaria(TipoChave x, TipoTabela T)
{ TipoIndice i , Esq, Dir ;
i f (T>n == 0)
return 0;
else
{ Esq = 1;
Dir = T>n;
do
{ i = (Esq + Dir ) / 2;
i f ( x > T>Item[ i ] .Chave)
Esq = i + 1;
else Dir = i 1;
} while ( x ! = T>Item[ i ] .Chave && Esq <= Dir ) ;
i f ( x == T>Item[ i ] .Chave) return i ; else return 0;
}
}
Pesquisa para a chave G:
1 2 3 4 5 6 7 8
A B C D E F G H
E F G H
G H
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 5.2 14
Pesquisa Binria: Anlise
A cada iterao do algoritmo, o tamanho da tabela dividido ao meio.
Logo: o nmero de vezes que o tamanho da tabela dividido ao meio
cerca de log n.
Ressalva: o custo para manter a tabela ordenada alto:
a cada insero na posio p da tabela implica no deslocamento dos
registros a partir da posio p para as posies seguintes.
Consequentemente, a pesquisa binria no deve ser usada em
aplicaes muito dinmicas.
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 5.3 15
rvores de Pesquisa
A rvore de pesquisa uma estrutura de dados muito eciente para
armazenar informao.
Particularmente adequada quando existe necessidade de considerar
todos ou alguma combinao de:
1. Acesso direto e sequencial ecientes.
2. Facilidade de insero e retirada de registros.
3. Boa taxa de utilizao de memria.
4. Utilizao de memria primria e secundria.
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 5.3.1 16
rvores Binrias de Pesquisa sem Balanceamento
Para qualquer n que contenha um registro
R
E D
Temos a relao invariante
E
R D
1. Todos os registros com chaves menores esto na subrvore
esquerda.
2. Todos os registros com chaves maiores esto na subrvore
direita.
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 5.3.1 17
rvores Binrias de Pesquisa sem Balanceamento
2 4 6
3 7
5
1
O nvel do n raiz 0.
Se um n est no nvel i ento a raiz de suas subrvores esto no
nvel i + 1.
A altura de um n o comprimento do caminho mais longo deste n
at um n folha.
A altura de uma rvore a altura do n raiz.
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 5.3.1 18
Implementao do Tipo Abstrato de Dados Dicionrio
usando a Estrutura de Dados rvore Binria de Pesquisa
Estrutura de dados:
typedef long TipoChave;
typedef struct TipoRegistro {
TipoChave Chave;
/ outros componentes /
} TipoRegistro;
typedef struct TipoNo TipoApontador;
typedef struct TipoNo {
TipoRegistro Reg;
TipoApontador Esq, Dir ;
} TipoNo;
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 5.3.1 19
Procedimento para Pesquisar na rvore Uma Chave x
Compare-a com a chave que est na raiz.
Se x menor, v para a subrvore esquerda.
Se x maior, v para a subrvore direita.
Repita o processo recursivamente, at que a chave procurada seja
encontrada ou um n folha atingido.
Se a pesquisa tiver sucesso o contedo retorna no prprio registro x.
void Pesquisa(TipoRegistro x, TipoApontador p)
{ i f (p == NULL)
{ pr i nt f ( "Erro: Registro nao esta presente na arvore\n" ) ; return; }
i f ( x>Chave < (p)>Reg.Chave)
{ Pesquisa(x, &(p)>Esq) ; return; }
i f ( x>Chave > (p)>Reg.Chave) Pesquisa(x, &(p)>Dir ) ;
else x = (p)>Reg;
}
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 5.3.1 20
Procedimento para Inserir na rvore
Atingir um apontador nulo em um processo de pesquisa signica uma
pesquisa sem sucesso.
O apontador nulo atingido o ponto de insero.
void Insere(TipoRegistro x, TipoApontador p)
{ i f (p == NULL)
{ p = (TipoApontador)malloc(sizeof(TipoNo) ) ;
(p)>Reg = x; ( p)>Esq = NULL; ( p)>Dir = NULL;
return;
}
i f ( x.Chave < (p)>Reg.Chave)
{ Insere(x, &(p)>Esq) ; return; }
i f ( x.Chave > (p)>Reg.Chave)
Insere(x, &(p)>Dir ) ;
else pr i nt f ( "Erro : Registro j a existe na arvore\n" ) ;
}
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 5.3.1 21
Procedimentos para Inicializar e Criar a rvore
void I ni ci al i za (TipoApontador Dicionario)
{ Dicionario = NULL; }
end; { I ni ci al i za }
{Entra aqui a denio dos tipos mostrados no slide 18 }
{Entram aqui os procedimentos Insere e Inicializa }
int main( int argc, char argv[ ] )
{ TipoDicionario Dicionario ; TipoRegistro x;
I ni ci al i za(&Dicionario) ;
scanf ( "%d%[^\n] " , &x.Chave) ;
while(x.Chave > 0)
{ Insere(x,&Dicionario) ;
scanf ( "%d%[^\n] " , &x.Chave) ;
}
}
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 5.3.1 22
Procedimento para Retirar x da rvore
Alguns comentrios:
1. A retirada de um registro no to simples quanto a insero.
2. Se o n que contm o registro a ser retirado possui no mximo um
descendente a operao simples.
3. No caso do n conter dois descendentes o registro a ser retirado
deve ser primeiro:
substitudo pelo registro mais direita na subrvore esquerda;
ou pelo registro mais esquerda na subrvore direita.
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 5.3.1 23
Exemplo da Retirada de um Registro da rvore
2 4 6
3 7
5
1
Assim: para retirar o registro com chave 5 na rvore basta troc-lo pelo
registro com chave 4 ou pelo registro com chave 6, e ento retirar o n que
recebeu o registro com chave 5.
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 5.3.1 24
Procedimento para Retirar x da rvore
void Antecessor(TipoApontador q, TipoApontador r )
{ i f ( ( r)>Dir ! = NULL)
{ Antecessor(q, &(r)>Dir ) ;
return;
}
q>Reg = ( r)>Reg;
q = r ;
r = ( r)>Esq;
free(q) ;
}
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 5.3.1 25
Procedimento para Retirar x da rvore
void Retira(TipoRegistro x, TipoApontador p)
{ TipoApontador Aux;
i f (p == NULL) { pr i nt f ( "Erro : Registro nao esta na arvore\n" ) ; return; }
i f ( x.Chave < (p)>Reg.Chave) { Retira(x, &(p)>Esq) ; return; }
i f ( x.Chave > (p)>Reg.Chave) { Retira(x, &(p)>Dir ) ; return; }
i f ( ( p)>Dir == NULL)
{ Aux = p; p = (p)>Esq;
free(Aux) ; return;
}
i f ( ( p)>Esq ! = NULL) { Antecessor(p, &(p)>Esq) ; return; }
Aux = p; p = (p)>Dir ;
free(Aux) ;
}
Obs.: proc. recursivo Antecessor s ativado quando o n que
contm registro a ser retirado possui 2 descendentes. Soluo usada
por Wirth, 1976, p.211.
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 5.3.1 26
Outro Exemplo de Retirada de N
bye
and
be
easy
to
and
to
be
to
be
and
be
and
to
bye
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 5.3.1 27
Caminhamento Central
Aps construda a rvore, pode ser necessrio percorrer todos os
registros que compem a tabela ou arquivo.
Existe mais de uma ordem de caminhamento em rvores, mas a mais
til a chamada ordem de caminhamento central.
O caminhamento central mais bem expresso em termos recursivos:
1. caminha na subrvore esquerda na ordem central;
2. visita a raiz;
3. caminha na subrvore direita na ordem central.
Uma caracterstica importante do caminhamento central que os ns
so visitados de forma ordenada.
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 5.3.1 28
Caminhamento Central
void Central (TipoApontador p)
{ i f (p == NULL) return;
Central (p>Esq) ;
pr i nt f ( "%l d \n" , p>Reg.Chave) ;
Central (p>Dir ) ;
}
Percorrer a rvore usando ca-
minhamento central recupera,
na ordem: 1, 2, 3, 4, 5, 6, 7.
2 4 6
3 7
5
1
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 5.3.1 29
Anlise
O nmero de comparaes em uma pesquisa com sucesso:
melhor caso : C(n) = O(1)
pior caso : C(n) = O(n)
caso mdio : C(n) = O(log n)
O tempo de execuo dos algoritmos para rvores binrias de
pesquisa dependem muito do formato das rvores.
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 5.3.1 30
Anlise
1. Para obter o pior caso basta que as chaves sejam inseridas em ordem
crescente ou decrescente. Neste caso a rvore resultante uma lista
linear, cujo nmero mdio de comparaes (n + 1)/2.
2. Para uma rvore de pesquisa randmica o nmero esperado de
comparaes para recuperar um registro qualquer cerca de
1, 39 log n, apenas 39% pior que a rvore completamente balanceada.
Uma rvore A com n chaves possui n + 1 ns externos e estas n
chaves dividem todos os valores possveis em n + 1 intervalos. Uma
insero em A considerada randmica se ela tem probabilidade igual
de acontecer em qualquer um dos n + 1 intervalos.
Uma rvore de pesquisa randmica com n chaves uma rvore
construida atravs de n inseres randmicas sucessivas em uma
rvore inicialmente vazia.
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 5.3.2 31
rvores Binrias de Pesquisa com Balanceamento
rvore completamente balanceada ns externos aparecem em no
mximo dois nveis adjacentes.
Minimiza tempo mdio de pesquisa para uma distribuio uniforme
das chaves, onde cada chave igualmente provvel de ser usada em
uma pesquisa.
Contudo, custo para manter a rvore completamente balanceada aps
cada insero muito alto.
Para inserir a chave 1 na rvore esquerda e obter a rvore direita
necessrio movimentar todos os ns da rvore original.
2 4 6 1 3 5 7
3 7
5
2 6
4
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 5.3.2 32
Uma Forma de Contornar este Problema
Procurar soluo intermediria que possa manter rvore
quase-balanceada, em vez de tentar manter a rvore completamente
balanceada.
Objetivo: Procurar obter bons tempos de pesquisa, prximos do
tempo timo da rvore completamente balanceada, mas sem pagar
muito para inserir ou retirar da rvore.
Heursticas: existem vrias heursticas baseadas no princpio acima.
Gonnet e Baeza-Yates (1991) apresentam algoritmos que utilizam
vrios critrios de balanceamento para rvores de pesquisa, tais como
restries impostas:
na diferena das alturas de subrvores de cada n da rvore,
na reduo do comprimento do caminho interno
ou que todos os ns externos apaream no mesmo nvel.
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 5.3.2 33
Uma Forma de Contornar este Problema
Comprimento do caminho interno: corresponde soma dos
comprimentos dos caminhos entre a raiz e cada um dos ns internos
da rvore.
Por exemplo, o comprimento do caminho interno da rvore esquerda
na gura do slide 31 8 = (0 + 1 + 1 + 2 + 2 + 2).
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 5.4 53
Pesquisa Digital
Pesquisa digital baseada na representao das chaves como uma
sequncia de caracteres ou de dgitos.
Os mtodos de pesquisa digital so particularmente vantajosos
quando as chaves so grandes e de tamanho varivel.
Um aspecto interessante quanto aos mtodos de pesquisa digital a
possibilidade de localizar todas as ocorrncias de uma determinada
cadeia em um texto, com tempo de resposta logartmico em relao ao
tamanho do texto.
Trie
Patrcia
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 5.4.1 54
Trie
Uma trie uma rvore M-ria cujos ns so vetores de M
componentes com campos correspondentes aos dgitos ou caracteres
que formam as chaves.
Cada n no nvel i representa o conjunto de todas as chaves que
comeam com a mesma sequncia de i dgitos ou caracteres.
Este n especica uma ramicao com M caminhos dependendo do
(i + 1)-simo dgito ou caractere de uma chave.
Considerando as chaves como sequncia de bits (isto , M = 2), o
algoritmo de pesquisa digital semelhante ao de pesquisa em rvore,
exceto que, em vez de se caminhar na rvore de acordo com o
resultado de comparao entre chaves, caminha-se de acordo com os
bits de chave.
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 5.4.1 55
Exemplo
Dada as chaves de 6 bits:
B = 010010
C = 010011
H = 011000
J = 100001
M = 101000
0
0
1 0
0
0
1
1
1
1
0 1
C B
H J
Q
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 5.4.1 56
Insero das Chaves W e K na Trie Binria
0
0
1 0
0
0
1
1
1
1
0 1
C B
H J
Q
Faz-se uma pesquisa na rvore com a chave a ser inserida. Se o n externo em
que a pesquisa terminar for vazio, cria-se um novo n externo nesse ponto
contendo a nova chave. Exemplo: a insero da chave W = 110110.
Se o n externo contiver uma chave cria-se um ou mais ns internos cujos
descendentes contero a chave j existente e a nova chave. Exemplo: insero
da chave K = 100010.
0
0
0
0
0
0
0
1
1
1
1
1
1
1
0 1
C B
H
J K
Q
W
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 5.4.1 57
Consideraes Importantes sobre as Tries
O formato das tries, diferentemente das rvores binrias comuns, no
depende da ordem em que as chaves so inseridas e sim da estrutura
das chaves atravs da distribuio de seus bits.
Desvantagem:
Uma grande desvantagem das tries a formao de caminhos de
uma s direo para chaves com um grande nmero de bits em
comum.
Exemplo: Se duas chaves diferirem somente no ltimo bit, elas
formaro um caminho cujo comprimento igual ao tamanho delas,
no importando quantas chaves existem na rvore.
Caminho gerado pelas chaves B e C.
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 5.4.2 58
Patricia - Practical Algorithm To Retrieve Information
Coded In Alphanumeric
Criado por Morrison D. R. 1968 para aplicao em recuperao de
informao em arquivos de grande porte.
Knuth D. E. 1973 novo tratamento algoritmo.
Reapresentou-o de forma mais clara como um caso particular de
pesquisa digital, essencialmente, um caso de rvore trie binria.
Sedgewick R. 1988 apresentou novos algoritmos de pesquisa e de
insero baseados nos algoritmos propostos por Knuth.
Gonnet, G.H e Baeza-Yates R. 1991 propuzeram tambm outros
algoritmos.
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 5.4.2 59
Mais sobre Patricia
O algoritmo para construo da rvore Patricia baseado no mtodo
de pesquisa digital, mas sem o inconveniente citado para o caso das
tries.
O problema de caminhos de uma s direo eliminado por meio de
uma soluo simples e elegante: cada n interno da rvore contm o
ndice do bit a ser testado para decidir qual ramo tomar.
Exemplo: dada as chaves de 6 bits:
B = 010010
C = 010011
H = 011000
J = 100001
Q = 101000
B C
H J
Q
3
1
3
6
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 5.4.2 60
Insero da Chave K
B C
H J
Q
3
1
3
6
B C
H
3
6 5
Q
3
1
J K
Para inserir a chave K = 100010 na rvore esquerda, a pesquisa inicia pela raiz e
termina quando se chega ao n externo contendo J.
Os ndices dos bits nas chaves esto ordenados da esquerda para a direita. Bit de
ndice 1 de K 1 a subrvore direita Bit de ndice 3 subrvore esquerda que
neste caso um n externo.
Chaves J e K mantm o padro de bits 1x0xxx, assim como qualquer outra chave
que seguir este caminho de pesquisa.
Novo n interno repe o n J, e este com n K sero os ns externos descendentes.
O ndice do novo n interno dado pelo 1
o
bit diferente das 2 chaves em questo,
que o bit de ndice 5. Para determinar qual ser o descendente esquerdo e o
direito, verique o valor do bit 5 de ambas as chaves.
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 5.4.2 61
Insero da Chave W
A insero da chave W = 110110 ilustra um outro aspecto.
Os bits das chaves K e W so comparados a partir do primeiro para
determinar em qual ndice eles diferem (nesse casod os de ndice 2).
Portanto: o ponto de insero agora ser no caminho de pesquisa
entre os ns internos de ndice 1 e 3.
Cria-se a um novo n interno de ndice 2, cujo descendente direito
um n externo contendo W e cujo descendente esquerdo a
subrvore de raiz de ndice 3.
B C
H
3
6
2
1
5
J K
3 W
Q
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 5.4.2 62
Estrutura de Dados
#define D 8 / depende de TipoChave /
typedef unsigned char TipoChave; / a defi ni r , depende da aplicacao /
typedef unsigned char TipoIndexAmp;
typedef unsigned char TipoDib;
typedef enum {
Interno , Externo
} TipoNo;
typedef struct TipoPatNo TipoArvore;
typedef struct TipoPatNo {
TipoNo nt ;
union {
struct {
TipoIndexAmp Index;
TipoArvore Esq, Dir ;
} NInterno ;
TipoChave Chave;
} NO;
} TipoPatNo;
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 5.4.2 63
Funes Auxiliares
TipoDib Bi t (TipoIndexAmp i , TipoChave k)
{ / Retorna o iesimo bi t da chave k a part i r da esquerda /
int c, j ;
i f ( i == 0)
return 0;
else { c = k;
for ( j = 1; j <= D i ; j ++) c / = 2;
return ( c & 1);
}
}
short EExterno(TipoArvore p)
{ / Verifica se p^ e nodo externo /
return ( p>nt == Externo) ;
}
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 5.4.2 64
Procedimentos para Criar Ns Interno e Externo
TipoArvore CriaNoInt ( int i , TipoArvore Esq, TipoArvore Dir )
{ TipoArvore p;
p = (TipoArvore)malloc(sizeof(TipoPatNo) ) ;
p>nt = Interno ; p>NO. NInterno.Esq = Esq;
p>NO. NInterno. Dir = Dir ; p>NO. NInterno. Index = i ;
return p;
}
TipoArvore CriaNoExt(TipoChave k)
{ TipoArvore p;
p = (TipoArvore)malloc(sizeof(TipoPatNo) ) ;
p>nt = Externo; p>NO.Chave = k; return p;
}
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 5.4.2 65
Algoritmo de Pesquisa
void Pesquisa(TipoChave k, TipoArvore t )
{ i f ( EExterno( t ) )
{ i f ( k == t>NO.Chave)
pr i nt f ( "Elemento encontrado\n" ) ;
else pr i nt f ( "Elemento nao encontrado\n" ) ;
return;
}
i f ( Bi t ( t>NO. NInterno. Index, k) == 0)
Pesquisa(k, t>NO. NInterno.Esq) ;
else Pesquisa(k, t>NO. NInterno. Dir ) ;
}
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 5.4.2 66
Descrio Informal do Algoritmo de Insero
Cada chave k inserida de acordo com os passos abaixo, partindo da
raiz:
1. Se a subrvore corrente for vazia, ento criado um n externo
contendo a chave k (isto ocorre somente na insero da primeira
chave) e o algoritmo termina.
2. Se a subrvore corrente for simplesmente um n externo, os bits da
chave k so comparados, a partir do bit de ndice imediatamente
aps o ltimo ndice da sequncia de ndices consecutivos do
caminho de pesquisa, com os bits correspondentes da chave k
deste n externo at encontrar um ndice i cujos bits diram. A
comparao dos bits a partir do ltimo ndice consecutivo melhora
consideravelmente o desempenho do algoritmo. Se todos forem
iguais, a chave j se encontra na rvore e o algoritmo termina;
seno, vai-se para o Passo 4.
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 5.4.2 67
Descrio Informal do Algoritmo de Insero
Continuao:
3. Se a raiz da subrvore corrente for um n interno, vai-se para a
subrvore indicada pelo bit da chave k de ndice dado pelo n
corrente, de forma recursiva.
4. Depois so criados um n interno e um n externo: o primeiro
contendo o ndice i e o segundo, a chave k. A seguir, o n interno
ligado ao externo pelo apontador de subrvore esquerda ou direita,
dependendo se o bit de ndice i da chave k seja 0 ou 1,
respectivamente.
5. O caminho de insero percorrido novamente de baixo para cima,
subindo com o par de ns criados no Passo 4 at chegar a um n
interno cujo ndice seja menor que o ndice i determinado no Passo
2. Este o ponto de insero e o par de ns inserido.
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 5.4.2 68
Algoritmo de insero
TipoArvore InsereEntre(TipoChave k, TipoArvore t , int i )
{ TipoArvore p;
i f ( EExterno(t ) | | i < ( t)>NO. NInterno. Index)
{ / cri a um novo no externo /
p = CriaNoExt(k) ;
i f ( Bi t ( i , k) == 1)
return ( CriaNoInt ( i , t , &p) ) ;
else return ( CriaNoInt ( i , &p, t ) ) ;
}
else
{ i f ( Bi t (( t)>NO. NInterno. Index, k) == 1)
(t)>NO. NInterno. Dir = InsereEntre(k,&(t)>NO. NInterno. Dir , i ) ;
else
(t)>NO. NInterno.Esq = InsereEntre(k,&(t)>NO. NInterno.Esq, i ) ;
return ( t ) ;
}
}
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 5.4.2 69
Algoritmo de insero
TipoArvore Insere(TipoChave k, TipoArvore t )
{ TipoArvore p; int i ;
i f ( t == NULL) return ( CriaNoExt(k) ) ;
else
{ p = t ;
while ( ! EExterno(p) )
{ i f ( Bi t (p>NO. NInterno. Index, k) == 1) p = p>NO. NInterno. Dir ;
else p = p>NO. NInterno.Esq;
}
/ acha o primeiro bi t diferente /
i = 1;
while ( ( i <= D) & ( Bi t ( ( int ) i , k) == Bi t ( ( int ) i , p>NO.Chave) ) )
i ++;
i f ( i > D) { pr i nt f ( "Erro: chave j a esta na arvore\n" ) ; return ( t ) ; }
else return ( InsereEntre(k, t , i ) ) ;
}
}
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 5.5 70
Transformao de Chave (Hashing)
Os registros armazenados em uma tabela so diretamente
endereados a partir de uma transformao aritmtica sobre a chave
de pesquisa.
Hash signica:
1. Fazer picadinho de carne e vegetais para cozinhar.
2. Fazer uma baguna. (Websters New World Dictionary)
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 5.5 71
Transformao de Chave (Hashing)
Um mtodo de pesquisa com o uso da transformao de chave
constitudo de duas etapas principais:
1. Computar o valor da funo de transformao, a qual transforma
a chave de pesquisa em um endereo da tabela.
2. Considerando que duas ou mais chaves podem ser transformadas
em um mesmo endereo de tabela, necessrio existir um mtodo
para lidar com colises.
Qualquer que seja a funo de transformao, algumas colises iro
ocorrer fatalmente, e tais colises tm de ser resolvidas de alguma
forma.
Mesmo que se obtenha uma funo de transformao que distribua os
registros de forma uniforme entre as entradas da tabela, existe uma
alta probabilidade de haver colises.
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 5.5 72
Transformao de Chave (Hashing)
O paradoxo do aniversrio (Feller,1968, p. 33), diz que em um grupo
de 23 ou mais pessoas, juntas ao acaso, existe uma chance maior do
que 50% de que 2 pessoas comemorem aniversrio no mesmo dia.
Assim, se for utilizada uma funo de transformao uniforme que
enderece 23 chaves randmicas em uma tabela de tamanho 365, a
probabilidade de que haja colises maior do que 50%.
A probabilidade p de se inserir N itens consecutivos sem coliso em
uma tabela de tamanho M :
p =
M 1
M

M 2
M
. . .
M N + 1
M
=
N

i=1
M i + 1
M
=
M!
(M N)!M
N
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 5.5 73
Transformao de Chave (Hashing)
Alguns valores de p para diferentes valores de N,onde M = 365.
N p
10 0,883
22 0,524
23 0,493
30 0,303
Para N pequeno a probabilidade p pode ser aproximada por
p
N(N1))
730
. Por exemplo, para N = 10 ento p 87, 7%.
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 5.5.1 74
Funes de Transformao
Uma funo de transformao deve mapear chaves em inteiros dentro
do intervalo [0..M 1], onde M o tamanho da tabela.
A funo de transformao ideal aquela que:
1. Seja simples de ser computada.
2. Para cada chave de entrada, qualquer uma das sadas possveis
igualmente provvel de ocorrer.
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 5.5.1 75
Mtodo mais Usado
Usa o resto da diviso por M.
h(K) = K mod M
onde K um inteiro correspondente chave.
Cuidado na escolha do valor de M. M deve ser um nmero primo,
mas no qualquer primo: devem ser evitados os nmeros primos
obtidos a partir de
b
i
j
onde b a base do conjunto de caracteres (geralmente b = 64 para
BCD, 128 para ASCII, 256 para EBCDIC, ou 100 para alguns cdigos
decimais), e i e j so pequenos inteiros.
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 5.5.1 76
Transformao de Chaves No Numricas
As chaves no numricas devem ser transformadas em nmeros:
K =
n

i=1
Chave[i] p[i]
n o nmero de caracteres da chave.
Chave[i] corresponde representao ASCII do i-simo caractere da
chave.
p[i] um inteiro de um conjunto de pesos gerados randomicamente
para 1 i n.
Vantagem de usar pesos: Dois conjuntos diferentes de p
1
[i] e p
2
[i],
1 i n, leva a duas funes h
1
(K) e h
2
(K) diferentes.
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 5.5.1 77
Transformao de Chaves No Numricas
void GeraPesos(TipoPesos p)
{ int i ;
struct timeval semente;
/ Ut i l i zar o tempo como semente para a funcao srand( ) /
gettimeofday(&semente, NULL) ;
srand( ( int ) (semente. tv_sec + 1000000semente. tv_usec) ) ;
for ( i = 0; i < n; i ++)
p[ i ] = 1+(int) (10000.0rand( ) / ( RAND_MAX+1.0));
}
typedef char TipoChave[N] ;
TipoIndice h(TipoChave Chave, TipoPesos p)
{ int i ; unsigned int Soma = 0;
int comp = strl en(Chave) ;
for ( i = 0; i < comp; i ++) Soma += (unsigned int )Chave[ i ] p[ i ] ;
return (Soma%M) ;
}
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 5.5.1 78
Transformao de Chaves No Numricas: Nova Verso
Modicao no clculo da funo h para evitar a multiplicao da
representao ASCII de cada caractere pelos pesos (Zobrist 1990).
Este um caso tpico de troca de espao por tempo.
Um peso diferente gerado randomicamente para cada um dos 256
caracteres ASCII possveis na isima posio da chave, para
1 i n.
#define TAMALFABETO 256
typedef unsigned TipoPesos[N] [ TAMALFABETO] ;
void GeraPesos(TipoPesos p) / Gera valores randomicos entre 1 e 10.000 /
{ int i , j ; struct timeval semente; / Ut i l i zar o tempo como semente /
gettimeofday(&semente, NULL) ;
srand( ( int ) (semente. tv_sec + 1000000 semente. tv_usec) ) ;
for ( i = 0; i < N; i ++)
for ( j = 0; j < TAMALFABETO; j ++)
p[ i ] [ j ] = 1 + ( int )(10000.0 rand( ) / ( RAND_MAX + 1. 0));
}
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 5.5.1 79
Transformao de Chaves No Numricas: Nova Verso
Implementao da funo hash de Zobrist:
Para obter h necessrio o mesmo nmero de adies da funo do
programa anterior, mas nenhuma multiplicao efetuada.
Isso faz com que h seja computada de forma mais eciente.
Nesse caso, a quantidade de espao para armazenar h O(n ||),
onde || representa o tamanho do alfabeto, enquanto que para a
funo do programa anterior O(n).
typedef char TipoChave[N] ;
TipoIndice h(TipoChave Chave, TipoPesos p)
{ int i ; unsigned int Soma = 0;
int comp = strl en(Chave) ;
for ( i = 0; i < comp; i ++) Soma += p[ i ] [ (unsigned int )Chave[ i ] ] ;
return (Soma%M) ;
}
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 5.5.2 80
Listas Encadeadas
Uma das formas de resolver as colises construir uma lista linear
encadeada para cada endereo da tabela. Assim, todas as chaves
com mesmo endereo so encadeadas em uma lista linear.
Exemplo: Se a i-sima letra do alfabeto representada pelo nmero i
e a funo de transformao h(Chave) = Chave mod M utilizada
para M = 7, o resultado da insero das chaves P E S Q U I S A na
tabela o seguinte:
h(A) = h(1) = 1, h(E) = h(5) = 5, h(S) = h(19) = 5, e assim por
diante.
T

nil
nil
U
A

nil
nil
P

I

nil
Q

nil
E

S

S

nil
6
5
4
3
2
1
0
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 5.5.2 81
Estrutura do Dicionrio para Listas Encadeadas
typedef char TipoChave[N] ;
typedef unsigned TipoPesos[N] [ TAMALFABETO] ;
typedef struct TipoItem {
/ outros componentes /
TipoChave Chave;
} TipoItem;
typedef unsigned int TipoIndice;
typedef struct TipoCelula TipoApontador;
typedef struct TipoCelula {
TipoItem Item;
TipoApontador Prox;
} TipoCelula;
typedef struct TipoLista {
TipoCelula Primeiro, Ultimo;
} TipoLista;
typedef TipoLista TipoDicionario[M] ;
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 5.5.2 82
Operaes do Dicionrio Usando Listas Encadeadas
void I ni ci al i za ( TipoDicionario T)
{ int i ;
for ( i = 0; i < M; i ++) FLVazia(&T[ i ] ) ;
}
TipoApontador Pesquisa(TipoChave Ch, TipoPesos p, TipoDicionario T)
{ / TipoApontador de retorno aponta para o item anterior da l i st a /
TipoIndice i ; TipoApontador Ap;
i = h(Ch, p) ;
i f ( Vazia(T[ i ] ) ) return NULL; / Pesquisa sem sucesso /
else
{ Ap = T[ i ] . Primeiro;
while (Ap>Prox>Prox ! = NULL &&
strncmp(Ch, Ap>Prox>Item.Chave, sizeof(TipoChave) ) )
Ap = Ap>Prox;
i f ( ! strncmp(Ch, Ap>Prox>Item.Chave, sizeof(TipoChave) ) ) return Ap;
else return NULL; / Pesquisa sem sucesso /
}
}
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 5.5.2 83
Operaes do Dicionrio Usando Listas Encadeadas
void Insere(TipoItem x, TipoPesos p, TipoDicionario T)
{
i f ( Pesquisa(x.Chave, p, T) == NULL)
Ins(x, &T[h(x.Chave, p) ] ) ;
else pr i nt f ( " Registro j a esta presente\n" ) ;
}
void Retira(TipoItem x, TipoPesos p, TipoDicionario T)
{
TipoApontador Ap;
Ap = Pesquisa(x.Chave, p, T) ;
i f (Ap == NULL)
pr i nt f ( " Registro nao esta presente\n" ) ;
else Ret(Ap, &T[h(x.Chave, p)] , &x) ;
}
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 5.5.2 84
Anlise
Assumindo que qualquer item do conjunto tem igual probabilidade de
ser endereado para qualquer entrada de T, ento o comprimento
esperado de cada lista encadeada N/M, onde N representa o
nmero de registros na tabela e M o tamanho da tabela.
Logo: as operaes Pesquisa, Insere e Retira custam O(1 +N/M)
operaes em mdia, onde a constante 1 representa o tempo para
encontrar a entrada na tabela e N/M o tempo para percorrer a lista.
Para valores de M prximos de N, o tempo se torna constante, isto ,
independente de N.
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 5.5.3 85
Endereamento Aberto
Quando o nmero de registros a serem armazenados na tabela puder
ser previamente estimado, ento no haver necessidade de usar
apontadores para armazenar os registros.
Existem vrios mtodos para armazenar N registros em uma tabela
de tamanho M > N, os quais utilizam os lugares vazios na prpria
tabela para resolver as colises. (Knuth, 1973, p.518)
No Endereamento aberto todas as chaves so armazenadas na
prpria tabela, sem o uso de apontadores explcitos.
Existem vrias propostas para a escolha de localizaes alternativas.
A mais simples chamada de hashing linear, onde a posio h
j
na
tabela dada por:
h
j
= (h(x) +j) mod M, para 1 j M 1.
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 5.5.3 86
Exemplo
Se a i-sima letra do alfabeto representada pelo nmero i e a funo
de transformao h(Chave) = Chave mod M utilizada para M = 7.
ento o resultado da insero das chaves L U N E S na tabela,
usando hashing linear para resolver colises mostrado abaixo.
Por exemplo, h(L) = h(12) = 5, h(U) = h(21) = 0, h(N) = h(14) = 0,
h(E) = h(5) = 5, e h(S) = h(19) = 5.
T
U
N
S
L
E
6
5
4
3
2
1
0
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 5.5.3 87
Estrutura do Dicionrio Usando Endereamento Aberto
#define VAZIO " ! ! ! ! ! ! ! ! ! ! "
#define RETIRADO ""
#define M 7
#define N 11 / Tamanho da chave /
typedef unsigned int TipoApontador;
typedef char TipoChave[N] ;
typedef unsigned TipoPesos[N] ;
typedef struct TipoItem {
/ outros componentes /
TipoChave Chave;
} TipoItem;
typedef unsigned int TipoIndice;
typedef TipoItem TipoDicionario[M] ;
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 5.5.3 88
Operaes do Dicionrio Usando Endereamento Aberto
void I ni ci al i za ( TipoDicionario T)
{ int i ;
for ( i = 0; i < M; i ++) memcpy(T[ i ] .Chave, VAZIO, N) ;
}
TipoApontador Pesquisa(TipoChave Ch, TipoPesos p, TipoDicionario T)
{ unsigned int i = 0; unsigned int I ni ci al ;
I ni ci al = h(Ch, p) ;
while ( strcmp(T[ ( I ni ci al + i ) %M] .Chave, VAZIO) != 0 &&
strcmp (T[ ( I ni ci al + i ) %M] .Chave, Ch) != 0 && i < M)
i ++;
i f ( strcmp( T[ ( I ni ci al + i ) %M] .Chave, Ch) == 0)
return ( ( I ni ci al + i ) %M) ;
else return M; / Pesquisa sem sucesso /
}
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 5.5.3 89
Operaes do Dicionrio Usando Endereamento Aberto
void Insere(TipoItem x, TipoPesos p, TipoDicionario T)
{ unsigned int i = 0; unsigned int I ni ci al ;
i f ( Pesquisa(x.Chave, p,T) < M) { pr i nt f ( "Elemento j a esta presente\n" ) ; return; }
I ni ci al = h(x.Chave, p) ;
while ( strcmp(T[ ( I ni ci al + i ) %M] .Chave, VAZIO) != 0 &&
strcmp(T[ ( I ni ci al + i ) %M] .Chave, RETIRADO) != 0 && i < M) i ++;
i f ( i < M)
{ strcpy(T[ ( I ni ci al + i ) %M] .Chave, x.Chave) ;
/ Copiar os demais campos de x, se existirem / }
else pr i nt f ( " Tabela cheia\n" ) ;
}
void Retira(TipoChave Ch, TipoPesos p, TipoDicionario T)
{ TipoIndice i ;
i = Pesquisa(Ch, p, T) ;
i f ( i < M)
memcpy(T[ i ] .Chave, RETIRADO, N) ;
else pr i nt f ( "Registro nao esta presente\n" ) ;
}
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 5.5.3 90
Anlise
Seja = N/M o fator de carga da tabela. Conforme demonstrado por
Knuth (1973), o custo de uma pesquisa com sucesso
C(n) =
1
2

1 +
1
1

O hashing linear sofre de um mal chamado agrupamento(clustering)


(Knuth, 1973, pp.520521).
Esse fenmeno ocorre na medida em que a tabela comea a car
cheia, pois a insero de uma nova chave tende a ocupar uma posio
na tabela que esteja contgua a outras posies j ocupadas, o que
deteriora o tempo necessrio para novas pesquisas.
Entretanto, apesar do hashing linear ser um mtodo relativamente
pobre para resolver colises os resultados apresentados so bons.
O melhor caso, assim como o caso mdio, O(1).
Projeto de Algoritmos - Cap.5 Pesquisa em Memria Primria 5.5.3 91
Vantagens e Desvantagens de Transformao da Chave
Vantagens:
Alta ecincia no custo de pesquisa, que O(1) para o caso mdio.
Simplicidade de implementao.
Desvantagens:
Custo para recuperar os registros na ordem lexicogrca das chaves
alto, sendo necessrio ordenar o arquivo.
Pior caso O(N).

You might also like