Você está na página 1de 33

Tabelas de Hash

 Basicamente é um vector, fixo ou dinâmico,


onde se armazenam elementos
 Cada elemento tem de estar associado a uma
chave
 Pode ser um número (int ou long)
 Ou outro tipo qualquer, como uma string
 Assume-se que não existem dois elementos
com a mesma chave!
 Um elemento não é colocado na primeira
posição livre do vector, mas sim num índice
determinado a partir da sua chave
 É ao cálculo desse índice que se dá o nome de
hash
 à função que faz essa conversão chama-se função de
hashing

Algoritmos e Estruturas de Dados F. Sérgio Barbosa

Tabelas de Hash
 As tabelas de hash são muito eficientes
nas operações de pesquisa
 As inserções são também muito rápidas
 Não permitem a ordenação de dados

Algoritmos e Estruturas de Dados F. Sérgio Barbosa

1
Tabelas de Hash – Endereçamento directo
 Nestas tabelas o vector tem tantas
posições como o número de elementos a
guardar
 Cada elemento tem assim uma posição
pré-determinada em função da sua
chave
Elementos possíveis * 0 Tabela de hash de
1 Acesso directo
1 1
* 2
0 6 3 3

2 * 4
5 * 5
3
4 6 6
8 * 7
7
8 8

Algoritmos e Estruturas de Dados F. Sérgio Barbosa

Tabelas de Hash – Endereçamento directo


 As operações de inserção, procura e
eliminação são assim extremamente
rápidas
 Só dão para tabelas em que se sabe à
partida quantos elementos podem existir
 Só dão para números de elementos
relativamente pequenos

Algoritmos e Estruturas de Dados F. Sérgio Barbosa

2
Tabelas de Hash – Endereçamento directo
 Classe para representar uma tabela de
hash com endereçamento directo
 Como se vai ver, nestes casos, não é
necessário guardar a chave junto com o
elemento
 Para simplificar vai-se usar chaves
inteiras

class TabelaHash {

// usar um array internamente


private Object aTabela[];

Algoritmos e Estruturas de Dados F. Sérgio Barbosa

Tabelas de Hash – Endereçamento directo


 Criar uma tabela
 É necessário indicar o número de
posições
class TabelaHash {

// usar um array internamente


private Object[] aTabela;

public TabelabHash( int nPos ){


// criar a tabela propriamente dita
aTabela = new Object[ nPos ];

// inicializar todas as posições a null (vazias)


// em java não é preciso
for( int i=0; i < aTabela.length; i++ )
aTabela[ i ] = null;
}
}

Algoritmos e Estruturas de Dados F. Sérgio Barbosa

3
Tabelas de Hash – Endereçamento directo
 Inserir na tabela
 É necessário indicar o elemento e a
chave
 A inserção é directa!

boolean inserir( int chave, Object oElem ) {

if( aTabela[chave] != null ) ) // já existe


return false;

aTabela[ chave ] = oElem;


return true;
}

Algoritmos e Estruturas de Dados F. Sérgio Barbosa

Tabelas de Hash – Endereçamento directo


 Procurar na tabela
 É necessário indicar a chave a procurar
 A procura é directa!

Object procura( int chave ) {

return aTabela[ chave ];


}

Algoritmos e Estruturas de Dados F. Sérgio Barbosa

4
Tabelas de Hash – Endereçamento directo
 Eliminar da tabela
 É necessário indicar a chave a eliminar
 A eliminação é directa!

boolean elimina( int chave ){

if( aTabela[ chave ] == null ) // ver se elemento não existe


return false;

aTabela[ chave ] = null;


return true;
}

Algoritmos e Estruturas de Dados F. Sérgio Barbosa

Tabelas de Hash – Endereçamento directo


 Limpar a tabela

void limpar( ) {

for( int i = 0; i < aTabela.length; i++ )


aTabela[ i ] = null;

Algoritmos e Estruturas de Dados F. Sérgio Barbosa

5
Tabelas de Hash
 Mas, e se o número de elementos a
colocar na tabela for maior que o
número de elementos da tabela?
 Tem-se de usar uma conversão da
chave para a posição da tabela
 É a esta conversão que se chama
hashing
 A escolha de uma boa função de
hashing é fundamental

Algoritmos e Estruturas de Dados F. Sérgio Barbosa

Tabelas de Hash
 Para o exemplo seguinte vai-se usar a
função
 Hash1( x ) = x % 13
Elementos possíveis Tabela de hash
0 13 Hash1( 13 ) = 0
1 1 Hash1( 1 ) = 1
2 Hash1( 28 ) = 2
54 2 28
20 50 1 3 3 Hash1( 3 ) = 3
17 3 * 4
31
6 * 5
0 22
32 8 6 6 Hash1( 6 ) = 6
43 * 7
5 13
8 8 Hash1( 8 ) = 8
4 28
* 9
7 23
* 10
* 11
* 12
Algoritmos e Estruturas de Dados F. Sérgio Barbosa

6
Tabelas de Hash
 Para o exemplo seguinte vai-se usar a
função
 Hash2( str ) = str.length() % 7
Elementos possíveis Tabela de hash
0 Métodos
e * 1
de 2 Ambientes

Micro 3 Computação
Desenho
Métodos 4 Programação
Gráfica
Computação 5 Micro
Análise
Ambientes * 6
Matemática Programação Hash2( “Micro” ) = 5
Hash2( “Métodos” ) = 0
Disciplina
Hash2( “Computação” ) = 3
Hash2( “Ambientes” ) = 2
Hash2( “Programação” ) = 4
Algoritmos e Estruturas de Dados F. Sérgio Barbosa

Tabelas de Hash
 E o que acontece se dois elementos
ficarem na mesma posição?
 Dá-se uma colisão!
 Como se resolve?
 Primeiro vamos tecer algumas
considerações sobre funções de hashing

Algoritmos e Estruturas de Dados F. Sérgio Barbosa

7
Tabelas de Hash
 As funções de hashing:
 Devem ser rápidas
 Devem evitar ao máximo as colisões:
 Cada posição da tabela deve ter a mesma
probabilidade de ser escolhida
 A escolha de uma dada função de
hashing não é pois uma decisão directa

Algoritmos e Estruturas de Dados F. Sérgio Barbosa

Tabelas de Hash
 As funções de hashing numéricas:
 São muito utilizadas funções do género
de Hash1
 Hash( x ) = x % valor

 Obtêm-se melhores resultados se valor


for um número primo
 Isso implica que se deve escolher um
tamanho de tabela (valor) que seja um
número primo

 Ver na bibliografia outros tipos de


funções
Algoritmos e Estruturas de Dados F. Sérgio Barbosa

8
Tabelas de Hash
 As funções de hashing não numéricas:
 Faz-se a conversão para um valor
numérico (exemplo de hash2) e depois
aplicam-se as funções numéricas.
 A função que faz a conversão terá de
produzir valores que sejam o mais
diferentes possíveis de modo a
resultarem menos colisões
 A função hash2 não é muito razoável,
para dicionários por exemplo.
 como a tabela é pequena é muito provável
que muitas strings fiquem na mesma
posição
Algoritmos e Estruturas de Dados F. Sérgio Barbosa

Tabelas de Hash
 As funções de hashing não numéricas:
 Para strings pode-se também processar alguns (ou
todos) os caracteres de modo a produzir um valor

long hashing( String str ){


long resultado = 0;
for( int i=0; i < str.length(); i++) // ver todos os caracteres
resultado = resultado*16+str.charAt( i );
return resultado % 31; // aplicar o método numérico
}

long hashing( String str ) {


long resultado = 0;
for( int i=0; i<3 && i < str.length(); i++ ) // ver 3 caracteres
resultado = resultado*16+str.charAt( i );
return resultado % 31; // aplicar o método numérico
}

Algoritmos e Estruturas de Dados F. Sérgio Barbosa

9
Tabelas de Hash
 Voltando ao problemas das colisões
 Como se resolve?
 Uma solução seria, em cada posição do
vector, colocar uma lista ligada de
elementos
 Inserções à cabeça
 Cada novo elemento é adicionado à cabeça
dessa lista
 Inserções ordenadas
 Cada novo elemento é adicionado na
posição correcta da lista
 O critério de ordenação será a chave

Algoritmos e Estruturas de Dados F. Sérgio Barbosa

Tabelas de Hash – inserção à cabeça


 Para o exemplo seguinte vai-se usar a
função
 Hash1( x ) = x % 13
Elementos possíveis Tabela de hash
* 0
1 14 1 *
2
54 * 2
20 50 1 3 3 *
17 3 * 4
31
6 * 5
0 22
8 6 19 6 *
32
43 * 7
5 14
8 8 *
4 19
* 9
7 23
* 10
* 11
* 12
Algoritmos e Estruturas de Dados F. Sérgio Barbosa

10
Tabelas de Hash – inserção ordenada
 Para o exemplo seguinte vai-se usar a
função
 Hash1( x ) = x % 13
Elementos possíveis Tabela de hash
* 0
1 1 14 *
2
54 * 2
20 50 1 3 3 *
17 3 * 4
31
6 * 5
0 22
8 6 6 19 *
32
43 * 7
5 14
8 8 *
4 19
* 9
7 23
* 10
* 11
* 12
Algoritmos e Estruturas de Dados F. Sérgio Barbosa

Tabelas de Hash
 Inserção ordenada vs Inserção à cabeça
 A inserção à cabeça é mais rápida
 Não é preciso percorrer a lista para saber onde
colocar o elemento
 Mas não garante que o elemento já lá esteja
 Nesse caso seria melhor inserção na cauda
 A inserção ordenada pode detectar se
elemento já existe
 Como percorre a lista pode detectar se já lá está,
sem perda de processamento
 A procura com inserção ordenada é mais
rápida
 Já que não é preciso percorrer a lista toda para
determinar se o elemento lá está ou não
 A inserção ordenada nem sempre é possível

Algoritmos e Estruturas de Dados F. Sérgio Barbosa

11
Tabelas de Hash
 Estrutura para representar uma tabela
de hash com listas
 Nestes caso são necessárias duas
estruturas:
 uma para a lista
 outra para a tabela
 Para efeitos de exemplo vai-se elaborar
uma tabela de automóveis
 A chave vai ser a Matrícula

Algoritmos e Estruturas de Dados F. Sérgio Barbosa

Tabelas de Hash
 Classes para representar uma tabela de
hash com listas
class TabelaAuto {

private ListaAuto[] aTabela;

class ListaAuto {

private class No {
String chave; // matricula
Automovel oAuto;
No prox;
}

Algoritmos e Estruturas de Dados F. Sérgio Barbosa

12
Tabelas de Hash
 Criar uma tabela de hash
 É necessário indicar o número de
posições
class TabelaAuto {

private ListaAuto[] aTabela;

public TabelaAuto( int nPos ) {


// criar o array de listas
aTabela = new ListaAuto[ nPos ];

// inicializar cada posição da tabela


// com uma lista vazia
for( int i=0; i < aTabela.length; i++ )
aTabela[ i ] = new ListaAuto();
}

Algoritmos e Estruturas de Dados F. Sérgio Barbosa

Tabelas de Hash
 Inserir na tabela
 É necessário indicar o elemento e a chave
 A inserção demora um tempo constante se
for à cabeça
 Se for ordenada varia de acordo com a
ocupação da lista
public boolean inserir( String chave, Automovel oAuto ) {

// fazer o hash da chave


int key = hashAuto( chave );

// ver se já existe na lista (não admite repetições)


if( aTabela[ key ].estaPresente( chave ) )
return false;

// adicionar a chave à lista


aTabela[ key ].inserirCabeca( oAuto );
return true;
}
Algoritmos e Estruturas de Dados F. Sérgio Barbosa

13
Tabelas de Hash
 Função de hash
 É necessário criar uma função para fazer
o hashing da matricula
 Seria necessário testar várias funções
para ver qual daria os melhores
resultados
private int hashAuto( String chave ) {

long resultado = 0;

// vêem-se 6 caracteres, no máximo


for( int i = 0; i < 6 && i < chave.length(); i++ )
resultado = resultado * 32 + chave.charAt( i );

// aplicar o método numérico


return resultado % aTabela.length;
}
Algoritmos e Estruturas de Dados F. Sérgio Barbosa

Tabelas de Hash
 Procurar na tabela
 É necessário indicar a chave a procurar
 A procura demora o tempo de procura
numa lista
 Melhores resultados se for inserção
ordenada

public Automovel procura( String chave ) {

int key = hashAuto( chave );

return aTabela[ key ].procura( chave );


}

Algoritmos e Estruturas de Dados F. Sérgio Barbosa

14
Tabelas de Hash
 Eliminar da tabela
 É necessário indicar a chave a eliminar
 A eliminação demora o tempo de
eliminação de uma lista
 Melhores resultados se for inserção
ordenada (por causa da pesquisa do
elemento)

public boolean elimina( String chave ) {

int key = hashAuto( chave );

return aTabela[ key ].elimina( chave );


}

Algoritmos e Estruturas de Dados F. Sérgio Barbosa

Tabelas de Hash
 Limpar a tabela

public void limpar( ) {

for( int i = 0; i < aTabela.length; i++ )


aTabela[ i ].limpar();

Algoritmos e Estruturas de Dados F. Sérgio Barbosa

15
Tabelas de Hash
 Usar a tabela

public static void main( String[] args ){

TabelaAuto osCarros = new TabelaAuto( 31 );

lerficheiro( “auto.txt”, osCarros );


fazerPesquisas( osCarros );
}

Algoritmos e Estruturas de Dados F. Sérgio Barbosa

Tabelas de Hash
 Usar a tabela
int lerFicheiro( String nomefich, TabelaAuto carros ) {

// vai retornar o nº de carros lidos


int nAutos = 0;

// abertura do ficheiro …
while( temInput ) {
// ler dados do carro
Automovel carro = new Automovel();
carro.setMatricula( … );
carro.setProprietario( … );

// adicionar o carro à tabela de hash


carros.inserir( carro.getMatricula(), carro );
nAutos++;
}

return nAutos;
}

Algoritmos e Estruturas de Dados F. Sérgio Barbosa

16
Tabelas de Hash
 Usar a tabela
void fazerPesquisas( TabelaAuto carros ) {

String matricula;
Automovel carro;

consola.print( “Introduza Matricula a pesquisar” );


matricula = consola.readString();
while( matricula.length() == 6 ) {
carro = carros.procura( matricula );
if( carro != null )
mostraInfoAuto( carro );
else
consola.println(“Não existe nenhum
carro com essa matricula\n\n”);

consola.print( “Introduza Matricula a pesquisar” );


matricula = consola.readString( );
}
}

Algoritmos e Estruturas de Dados F. Sérgio Barbosa

Tabelas de Hash
 E se se pretender uma tabela para
pesquisar, mas desta vez usando o
proprietário como chave?
 É preciso fazer outra tabela?
 Não, e essa é uma das vantagens de
usar chaves
 Desde que seja do mesmo tipo a mesma
tabela dá
 Assume-se que não existem dois
proprietários com o mesmo nome! Ou
que o mesmo proprietário tenha dois
automóveis!
Algoritmos e Estruturas de Dados F. Sérgio Barbosa

17
Tabelas de Hash
 Usar a tabela
int lerFicheiro( String nomefich, TabelaAuto carros ) {

int nAutos=0;

while( temInput ) {

Automovel carro = new Automovel();


carro.setMatricula( … );
carro.setProprietario( … );

// inserir, agora com o proprietário como chave


carros.inserir( carro.getProprietario(), carro );

nAutos++;
}

return nAutos;
}

Algoritmos e Estruturas de Dados F. Sérgio Barbosa

Tabelas de Hash
 Usar a tabela
void fazerPesquisas( TabelaAuto carros ) {
String propri;
Automovel carro;

consola.print( “Introduza proprietário a pesquisar” );


propri = consola.readString( );
while( propri.length >= 3 ) {
carro = carros.procura( propri );
if( carro != null )
mostraInfoAuto( carro );
else
consola.println(“Não existe nenhum carro
com esse proprietario\n\n”);

consola.print( “Introduza proprietário a pesquisar” );


propri = consola.readString();
}
}

Algoritmos e Estruturas de Dados F. Sérgio Barbosa

18
Tabelas de Hash – Endereçamento livre
 Neste caso os elementos são colocados
directamente na tabela (como no
endereçamento directo)
 No entanto, se a posição estiver ocupada, é
calculada uma nova posição até se encontrar
uma posição livre
 Técnica usada quando o número de elementos
possíveis é elevado, mas apenas uma parte
desses elementos é realmente usada
 Exemplo: nomes de variáveis (existem infinitas
combinações de nomes, mas num programa são
usados apenas alguns)

Algoritmos e Estruturas de Dados F. Sérgio Barbosa

Tabelas de Hash – Endereçamento livre


 Neste caso a tabela pode encher (quando todas
as posições estiverem ocupadas)
 Como se determina a nova posição, quando a
primeira está ocupada?
 E se a nova também estiver ocupada?
 E como deve ser a nova função?
 Existem 3 técnicas, e cada uma indica uma
função:
 Sondagem linear
 Sondagem quadrática
 Duplo hashing

Algoritmos e Estruturas de Dados F. Sérgio Barbosa

19
Tabelas de Hash – Endereçamento livre
 Nos três casos recorre-se a uma função,
que além da chave, depende também do
número de tentativas de colocação para
determinar a nova posição de colocação
 Todos os casos apresentam uma função
 hashEnderecoLivre( chave, tentativa )
 O que varia em cada caso é a função –
ou seja, o modo como se usa o número
de tentativas para calcular a nova
posição

Algoritmos e Estruturas de Dados F. Sérgio Barbosa

Tabelas de Hash – Endereçamento livre


 Nos três casos, e como os elementos
são guardados directamente na tabela,
é necessário estabelecer as seguintes
estruturas:
class TabelaLivre {

private ParElementoChave[] aTabela;

public TabelaHash( int nPos ){ … }

private class ParElementoChave {


Object item;
int chave;

ParElementoChave( int chv, Object elem) {…}


}

}
Algoritmos e Estruturas de Dados F. Sérgio Barbosa

20
Tabelas de Hash – Endereçamento livre
 Os construtores podiam ser assim:
class TabelaLivre {

private ParElementoChave[] aTabela;

public TabelaHash( int nPos ){


aTabela = new ParElementoChave[ nPos ];
for( int i = 0; i < aTabela.length; i++ )
aTabela[ i ] = null;
}

private class ParElementoChave {


Object item;
int chave;

ParElementoChave( int chv, Object elem) {


chave = chv;
item = elem;
}
}

}
Algoritmos e Estruturas de Dados F. Sérgio Barbosa

Tabelas de Hash – Endereçamento livre


 Inserir numa tabela
boolean inserir ( int chave, Object oElem ) {

ParChaveElem par = new ParChaveElem( chave, oElem );

for( int i=0; i < aTabela.length; i++ ) {

// calcular o índice a usar na tabela


long key = hashEnderecoLivre( chave, i );

// ver se posição está livre


if( aTabela[ key ] == null ) {
aTabela[ key ] = par;
return true;
}
// se está ocupada ver se é pela mesma chave
else if( aTabela[ key ].chave == chave )
return false;
}

return false; // tabela cheia


}
Algoritmos e Estruturas de Dados F. Sérgio Barbosa

21
Tabelas de Hash – Endereçamento livre
 Sondagem linear:
 Quando uma posição está ocupada
tenta-se a seguinte, e assim por diante
até se encontrar uma posição livre
 A nova função de hash é assim:

 hashLinear( x, i ) = (hash( x ) + i ) % m

 Hash é a função de hashing normal


 m é o tamanho da tabela
 i é a tentativa de colocação e varia de 0 a m-1

Algoritmos e Estruturas de Dados F. Sérgio Barbosa

Tabelas de Hash
 Sondagem linear - exemplo:
 Hash( x ) = x % 13
Tabela de hash
* 0 Hash( 1 ) = 1 * 0 Hash( 14 ) = 1
1 1 1 1 Tentativa = 0
* 2 2 14 Posição = 1
1
1 3 3 3 3 Tentativa = 1
3
3 * 4 Hash( 3 ) = 3 * 4 Posição = 2
14
14 * 5 * 5
27
27 * 6 * 6
* 7 2 * 7
2
* 8 * 8
* 9 * 9
* 10 * 10
* 11 * 11
* 12 * 12
0
Algoritmos e Estruturas de Dados /
F. Sérgio Barbosa

22
Tabelas de Hash
 Sondagem linear - exemplo:
 Hash( x ) = x % 13
Tabela de hash * 0 Hash( 2 ) = 2
* 0 Hash( 27 ) = 1 1 1
Tentativa = 0
1 1 2 14
Tentativa = 0 Posição = 2
2 14 1 3 3
Posição = 1
1 3 3 3 4 27
Tentativa = 1
3 4 27 Tentativa = 1 14 5 2
Posição = 3
14 * 5 Posição = 2 27 * 6
27 * 6 * 7 Tentativa = 2
Tentativa = 2 2
* 7 * 8 Posição = 4
2 Posição = 3
* 8 * 9
* 9 * 10 Tentativa = 3
Tentativa = 3 Posição = 5
* 10 * 11
Posição = 4
* 11 * 12
* 12
Algoritmos e Estruturas de Dados F. Sérgio Barbosa

Tabelas de Hash – Endereçamento livre


 Sondagem linear - inserção:
 A função de inserir na tabela é igual:
 A função hashEnderecoLivre é
substituída pela:

int hashSondagemLinear( int chave, int tentativa) {

// chamada à função de hash normal


int idx = hash( chave );

// associar agora à tentativa


return (idx + tentativa) % aTabela.length;
}

Algoritmos e Estruturas de Dados F. Sérgio Barbosa

23
Tabelas de Hash – Endereçamento livre
 Sondagem linear - inserção:
 Em alternativa podia-se usar a seguinte
função, menos versátil, mas mais
optimizada para a sondagem linear
boolean inserir( int chave, Object oElem ) {
ParChaveElemento par = new ParChaveElemento( chave, oElem );
int key = hash( chave );

for( int i = 0; i < aTabela.length; i++ ) {

// ver se está vazia


if( aTabela[ key ] == null ) {
aTabela[ key ] = par;
nElems++;
return true;
}
// ver se tem mesma posição
else if( aTabela[ key ].chave == chave )
return false;

key++;
if( key == aTabela.length )
key = 0;
}
return false; // tabela cheia
Algoritmos} e Estruturas de Dados F. Sérgio Barbosa

Tabelas de Hash – Endereçamento livre


 Sondagem quadrática:
 Quando uma posição está ocupada, na
tentativa seguinte calcula-se a nova
posição em função do número de
tentativas, da seguinte forma:

HashQuadratica( x, i ) = (Hash( x ) + a * i + b * i2 ) % m

 Hash é a função de hashing normal


 m o tamanho da tabela
 i é a tentativa de colocação e varia de 0 a m-1
 a e b são constantes a definir

Algoritmos e Estruturas de Dados F. Sérgio Barbosa

24
Tabelas de Hash – Endereçamento livre
 Sondagem Quadrática - inserção:
 A função de inserir na tabela é igual:
 A função HashEnderecoLivre é
substituída pela:
int hashSondagemQuad( int chave, int tentativa) {

// função de hash normal


int key = hash( chave );

// aplicação do método quadrático


return (key + A * tentativa + B * tentativa*tentativa ) %
aTabela.length;
// Nota: A e B são constantes
}

Algoritmos e Estruturas de Dados F. Sérgio Barbosa

Tabelas de Hash – Endereçamento livre


 Duplo hashing:
 Usam-se duas funções de hash
 Uma para fazer o hash “normal”
 Outra para calcular qual o deslocamento da próxima
posição em função da chave
 De referir que as anteriores eram sempre em função
da tentativa
 É a que apresenta melhores resultados
 Semelhante à linear mas o deslocamento não é de 1
e é diferente de chave para chave

hashDuplo( x, i ) = (hash1( x ) + i * hash2(x)) % m

 hash1 é a função de hashing normal


 hash2 é a função de hashing para determinar a
próxima posição
 m é o tamanho da tabela
 i é a tentativa de colocação e varia de 0 a m-1

Algoritmos e Estruturas de Dados F. Sérgio Barbosa

25
Tabelas de Hash
 Duplo hash - exemplo:
 Hash1( x ) = x % 13
 Hash2( x ) = 1+(x % 11)
Tabela de hash Hash1( 14 ) = 1
* 0 * 0 Hash2( 14 ) = 4
Hash1( 1 ) = 1
1 1 1 1 Tentativa = 0
* 2 * 2 Posição = 1
1 3 3 1 3 3
Hash( 3 ) = 3
3
? * 4 3 * 4 Tentativa = 1
14 * 5 14 5 14
Posição = 1+4 = 5
27 * 6 27 * 6
* 7 * 7
2 2
* 8 * 8
15 15
* 9 * 9
* 10 * 10
* 11 * 11
* 12 * 12
Algoritmos e Estruturas de Dados F. Sérgio Barbosa

Tabelas de Hash
 Duplo hash - exemplo:
 Hash1( x ) = x % 13
 Hash2( x ) = 1+(x % 11)
Hash1( 27 ) = 1
* 0 Hash2( 27 ) = 6 * 0 Hash1( 2 ) = 2
1 1 Tentativa = 0 1 1
* 2 Posição = 1 2 2
3
1 3 3 1 3
3 * 4 3 * 4
14
14 5 14 14 5
27 * 6 27 * 6
Tentativa = 1
7 27 7 27
2 Posição = 1+6 = 7 2
* 8 * 8
15 15
* 9 * 9
* 10 * 10
* 11 * 11
* 12 * 12
Algoritmos e Estruturas de Dados F. Sérgio Barbosa

26
Tabelas de Hash
 Duplo hash - exemplo:
 Hash1( x ) = x % 13
 Hash2( x ) = 1+(x % 11) Hash1( 15 ) = 2
* 0 Hash2( 15 ) = 5
1 1
2
Tentativa = 0
2 Posição = 2
1 3 3
3 * 4
14 5 14

27 * 6 Tentativa = 1
7 27
2 Posição = 2+5 = 7
* 8
15
* 9
* 10
* 11
12 15 Tentativa = 2
Posição = 2+2*5 = 12
Algoritmos e Estruturas de Dados F. Sérgio Barbosa

Tabelas de Hash – Endereçamento livre


 Duplo Hashing - inserção:
 A função de inserir na tabela é igual
 A função hashEnderecoLivre é
substituída pela:

int hashDuplo( int chave, int tentativa) {

// hash normal
int idx = hash1( chave );

// aplicação do método de duplo hash


return (idx + tentativa * hash2(chave)) % aTabela.length;
}

Algoritmos e Estruturas de Dados F. Sérgio Barbosa

27
Tabelas de Hash – Endereçamento livre
 Duplo Hashing - inserção:
 Em alternativa podia-se usar a seguinte
função, menos versátil, mas mais
optimizada para o duplo hashing
boolean inserir( int chave, Object oElem ) {
int key = hash1( chave );
int passo = hash2( chave );
ParChaveElemento par = new ParChaveElemento( chave, oElem );

for( int i=0; i < aTabela.length; i++ ) {


if( aTabela[ key ] == null ) {
aTabela[ key ] = par;
nElems++;
return true;
}
else if( aTabela[ key ].chave == chave )
return false;
key = (key + passo) % aTabela.length;
}
return false; // tabela cheia
}
Algoritmos e Estruturas de Dados F. Sérgio Barbosa

Tabelas de Hash – Endereçamento livre


 Duplo Hashing:
 Esta técnica é mais eficaz se se usarem
valores primos quer em Hash1, quer em
Hash2
 Os valores devem ser próximos entre si

 Pergunta:
 Porque razão a função Hash2 costuma
ter a forma
1 + (x % m)
em vez de
x % m ????
Algoritmos e Estruturas de Dados F. Sérgio Barbosa

28
Tabelas de Hash – Endereçamento livre
 Como se efectua a pesquisa no caso de
endereçamento livre?
 Determinar a posição onde o elemento
estaria
 Se ele lá está (chave tem de ser igual), achou
 Se não está lá, é necessário verificar na posição
seguinte
 A posição seguinte é determinada de acordo
com a técnica usada na inserção
(obviamente)
 Pára-se a procura quando:
 Se acha o elemento (chaves iguais)
 Se acha uma posição vazia – ele teria de estar até
esta posição inclusive
 Se percorre a lista toda

Algoritmos e Estruturas de Dados F. Sérgio Barbosa

Tabelas de Hash
 Sondagem linear – procura - exemplo:
 Hash( x ) = x % 13
Tabela de hash Hash( 2 ) = 2
* 0
2? 1 1
2 14 Tentativa = 0 Posição = 2 Chaves diferentes
3 3 Tentativa = 1 Posição = 3 Chaves diferentes
4 27 Tentativa = 2 Posição = 4 Chaves diferentes
5 2 Tentativa = 3 Posição = 5 Chaves Iguais => Encontrou
* 6
* 7
* 8
* 9
* 10
* 11
* 12
Algoritmos e Estruturas de Dados F. Sérgio Barbosa

29
Tabelas de Hash
 Sondagem linear – procura - exemplo:
 Hash( x ) = x % 13
Tabela de hash Hash( 4 ) = 4
* 0
4? 1 1
2 14
3 3
4 27 Tentativa = 0 Posição = 4 Chaves diferentes
5 2 Tentativa = 1 Posição = 5 Chaves diferentes
* 6 Tentativa = 3 Posição = 6 Posição livre => Não encontrou
* 7
* 8
* 9
* 10
* 11
* 12
Algoritmos e Estruturas de Dados F. Sérgio Barbosa

Tabelas de Hash – Endereçamento livre


 Como se efectua a pesquisa no caso de
endereçamento livre?
 Tem-se que usar a mesma técnica que
na inserção
 A função de pesquisa (genérica) seria
Object procurar( int chave ) {

for( int i = 0; i < aTabela.length; i++ ) {


long key = hashEnderecoLivre( chave, i );

if( aTabela[ key ] == null ) // Não está


return null;
else if( aTabela[ key ].chave == chave ) // achou
return aTabela[ key ].item;
}
return null; // chega aqui quando percorre a lista toda
}

Algoritmos e Estruturas de Dados F. Sérgio Barbosa

30
Tabelas de Hash – Endereçamento livre
 Como se efectua a remoção de um
elemento no caso de endereçamento
livre?
 Será que basta procurar o elemento e
depois colocar a posição a null?
 NÃO!! Como se pode ver no exemplo
seguinte

Algoritmos e Estruturas de Dados F. Sérgio Barbosa

Tabelas de Hash
 Sondagem linear – eliminar - exemplo:
 Hash( x ) = x % 13
Eliminar 27 Tabela de hash
* 0 * 0
1 1 1 1
2 14 2 14

3 3 3 3

Hash( 27 ) = 1 4 27 * 4
5 2 5 2
* 6 * 6
* 7 * 7
* 8 * 8
* 9 * 9
* 10 * 10
* 11 * 11
* 12 * 12
Algoritmos e Estruturas de Dados 2 F. Sérgio Barbosa

31
Tabelas de Hash
 Sondagem linear – eliminar - exemplo:
 Hash( x ) = x % 13

Procurar 2 Tabela de hash


* 0
1 1
2 14

3 3
Posição vazia
Hash( 2 ) = 2 * 4
5 2 Não encontra o 2 !!!
* 6
* 7
* 8
* 9
* 10
* 11
* 12 12
Algoritmos e Estruturas de Dados F. Sérgio Barbosa

Tabelas de Hash – Endereçamento livre


 Como se efectua a remoção de um elemento
no caso de endereçamento livre?
 Será que basta procurar o elemento e depois colocar
a posição a zero?
 NÃO!! Como se viu no exemplo anterior
 Então como se faz?
 Algumas técnicas:
 Definir um valor como APAGADO (1 por exemplo, já que
nenhum ponteiro aponta para a posição de memória 1)
 não é possível em Java
 Ao eliminar colocar um elemento com uma chave
especial que indica que está desocupada (exemplo, se o
elemento tem chave -1 significa que a posição está
vazia)

Algoritmos e Estruturas de Dados F. Sérgio Barbosa

32
Tabelas de Hash – Endereçamento livre
 Como se efectua a remoção de um elemento
no caso de endereçamento livre?
 As técnicas anteriores implicam alterar as funções de
inserção e de pesquisa
 A de inserção tem de procurar até achar uma posição
com o valor null (vazia) ou com a indicação de estar
apagada (APAGADO ou a chave especial)
 A de pesquisa tem de procurar até achar o elemento ou
uma posição com o valor null e ignorar as posições
apagadas
 Isto significa que se houver muitas remoções se
pode pesquisar a tabela toda só para determinar que
um elemento não está lá
 Por esta razão as tabelas de endereçamento livre são
pouco usadas quando a remoção de elementos é
frequente

Algoritmos e Estruturas de Dados F. Sérgio Barbosa

Tabelas de Hash – Endereçamento livre


 Limpar uma tabela
 É necessário libertar toda a memória
void limpar( ) {

for( int i = 0; i < aTabela.length; i++ )


aTabela[ i ] = null;

Algoritmos e Estruturas de Dados F. Sérgio Barbosa

33

Você também pode gostar