Você está na página 1de 37

Aula 11: Tabela Hash

DCC405-Estrutura de Dados II
Prof. Me. Acauan C. Ribeiro
DCC405-Estrutura de Dados II | Tabela Hash 2/37
O que você está procurando?

→ É uma pessoa? Um animal? Um objeto?

→ Como ele está vestido? Camisa? Calça? Algum


acessório?

→ Cor do cabelo? Aparência?

DCC405-Estrutura de Dados II | Tabela Hash 3/37


O que você está procurando?

→ É uma pessoa? Um animal? Um objeto?


Pessoa ...
→ Como ele está vestido? Camisa? Calça? Algum
acessório?

→ Cor do cabelo? Aparência?

DCC405-Estrutura de Dados II | Tabela Hash 4/37


O que você está procurando?

→ É uma pessoa? Um animal? Um objeto?


Pessoa ...
→ Como ele está vestido? Camisa? Calça? Algum
acessório?
Camisa listrada, óculos e gorro.
...
→ Cor do cabelo? Aparência?

DCC405-Estrutura de Dados II | Tabela Hash 5/37


O que você está procurando?

→ É uma pessoa? Um animal? Um objeto?


Pessoa ...
→ Como ele está vestido? Camisa? Calça? Algum
acessório?
Camisa listrada, óculos e gorro.
...
→ Cor do cabelo? Aparência?
Cabelo escuro e cara meio de bobo.

DCC405-Estrutura de Dados II | Tabela Hash 6/37


Roteiro
● Definição de Tabela Hash
● Aplicações
● Tratamento de colisões
● Complexidade
● Implementação

DCC405-Estrutura de Dados II | Tabela Hash 7/37


DCC405-Estrutura de Dados II | Tabela Hash 8/37
DCC405-Estrutura de Dados II | Tabela Hash 9/37
Pessoa, Camisa listrada, óculos e
O que você está procurando? gorro, Cabelo escuro e cara meio de
bobo.
→ É uma pessoa? Um animal? Um objeto?
Pessoa
Chave de pesquisa
→ Como ele está vestido? Camisa? Calça? Algum
acessório?
Camisa listrada, óculos e gorro.
Hash de busca
→ Cor do cabelo? Aparência?
Cabelo escuro e cara meio de bobo.

DCC405-Estrutura de Dados II | Tabela Hash 10/37


Pessoa ...
Animal ...
Homem, roupas listradas ...
Mulher, roupas de banho

Pessoa, Camisa listrada, óculos e gorro,


Cabelo escuro e cara meio de bobo.

DCC405-Estrutura de Dados II | Tabela Hash 11/37


Hash (keys) Values, buckets

Pessoa ...
Animal ...
Homem, roupas listradas ...
Mulher, roupas de banho

Pessoa, Camisa listrada, óculos e gorro,


Cabelo escuro e cara meio de bobo.

DCC405-Estrutura de Dados II | Tabela Hash 12/37


Definição de Tabela Hash

DCC405-Estrutura de Dados II | Tabela Hash 13/37


Tabela Hash - Definições

Ler:
→ Cap 11 – Tabelas de
Espalhamento – Livro:
Algoritmos - Teoria e
Prática - Cormen – 3nd.

DCC405-Estrutura de Dados II | Tabela Hash 14/37


Tabela Hash - Definições
As tabelas de hash são um tipo de estrutura de dados na qual o endereço ou o
valor de índice do elemento de dados é gerado a partir de uma função de hash.
Isso torna o acesso aos dados mais rápido, pois o valor do índice se comporta
como uma chave para o valor dos dados. Em outras palavras, a tabela de hash
armazena pares de valores-chave, mas a chave é gerada por meio de uma função de
hash.

DCC405-Estrutura de Dados II | Tabela Hash 15/37


Tabela Hash - Definições
● As tabelas de hash são um tipo de estrutura de dados usada na computação para armazenar dados
com eficiência. Eles são muito eficientes para armazenamento e velocidade de acesso - o que significa
que, nos piores casos, levará apenas tanto tempo realizando operações nos dados quanto a quantidade
de dados em si. Isso é muito eficiente, considerando que o tempo médio de processamento de qualquer
operação é executado imediatamente, independentemente do tamanho dos dados.

● As tabelas de hash caracteristicamente usam um chave-valor como método de armazenamento. Por


exemplo, muitos sites da Web usam nomes de usuário e senhas para identificar todos os usuários
autorizados, mas empregam tabelas de hash durante a autorização em vez de verificar a
correspondência de todos os usuários existentes.

DCC405-Estrutura de Dados II | Tabela Hash 16/37


Tabela Hash - Hash
● Hashing é um processo que utiliza uma função de hash para obter a chave da
tabela de hash e transformá-la em um índice que apontará para diferentes arrays de
buckets, que é onde as informações serão armazenadas. (Pense em um balde como
um lugar mais rápido para pesquisar coisas do que em uma lista realmente longa).
O hash funciona nos dois sentidos: ao armazenar dados, é a função que
determinará onde armazená-los e, ao recuperar dados, é a função que apontará
onde encontrá-los dentro da tabela de hash. A chave para uma boa tabela de hash é
escolher uma boa função de hash . Isso será abordado mais adiante na lição.

DCC405-Estrutura de Dados II | Tabela Hash 17/37


Tabela Hash – Função de Hash
● Uma função de hashing é responsável por transformar uma entrada de
comprimento variável em um hash. Não há função de hash padrão. Isso significa que
podemos desenvolver as funções de hash de acordo com as características gerais
esperadas de nossas entradas de dados.
● A imagem a seguir mostra, em alto nível, como funciona um mecanismo de hash:

DCC405-Estrutura de Dados II | Tabela Hash 18/37


Tabela Hash
● As tabelas de hash são estruturas de dados que associam
chaves específicas a valores correspondentes. Essas
tabelas geralmente são implementadas com uma matriz
associativa para armazenar os dados. Além disso, eles
usam uma função hash para calcular em qual ponto da
matriz os dados devem ser armazenados (o índice).


DCC405-Estrutura de Dados II | Tabela Hash 19/37
Tabela Hash
● Assim, podemos entender uma tabela de hash como uma pesquisa de valor-chave. Assim, dada uma chave
associada a um valor (dados), podemos recuperar o valor correspondente através de uma rápida pesquisa na
tabela.
● Por exemplo, podemos associar os nomes das pessoas com suas informações pessoais com uma tabela de hash.
Desta forma, os nomes das pessoas são nossas chaves brutas. Uma função de hash processa essas chaves brutas
para determinar seus índices correspondentes na tabela de hash, fornecendo acesso direto às informações pessoais.

DCC405-Estrutura de Dados II | Tabela Hash 20/37


Tabela Hash implementado nas Linguagens
● Com o tempo, as tabelas de hash tornaram-se muito populares no cenário da computação.
Assim, diferentes linguagens de programação movimentaram esforços para fornecer esse
tipo de estrutura de dados de forma nativa ou por meio de bibliotecas embutidas.
● Exemplos de estruturas desenvolvidas são os HashMaps em Java , a classe dict
(dicionário) em Python, a classe map em c++ e o alist em Lisp.

DCC405-Estrutura de Dados II | Tabela Hash 21/37


Tabela Hash – Função de Hash
● A função de dispersão que recebe um dado elemento e retorna um índice na
hash table. Esta função deve, idealmente, ser:
– rápida de calcular: caso contrário, o tempo ganho ao usar tabelas de dispersão seria
contrariado pelo hashing.
– envolver todos os bits da chave: idealmente, nenhum pedaço do input deve ser
inutilizado, de forma a obter um hashing que seja único para cada elemento.
– deve distribuir as chaves de forma uniforme e quase aleatória: numa função de
dispersão ideal, a probabilidade de ocorrer uma colisão (a função devolver o mesmo
índice para dois elementos distintos) deve ser 1/M, onde M é o tamanho da tabela.

DCC405-Estrutura de Dados II | Tabela Hash 22/37


Tabela Hash – Função de Hash
● Quando trabalhamos com números, é usual a nossa hash function trabalhar com
o módulo (resto) do elemento, k % M - desta maneira, considerando no máximo
M elementos, não deverá haver colisões. Devemos idealmente fazer uma estimativa
conservadora para o tamanho da tabela, de forma a não ser apanhados de surpresa
com quantidades anormais de colisões.

DCC405-Estrutura de Dados II | Tabela Hash 23/37


Tabela Hash – Função de Hash
● Trabalhar com cadeias de caracteres requer uma estratégia diferente - não
existe o "módulo de uma string", então temos de adaptar a nossa hash
function. O nosso objetivo, aqui, passa por calcular uma soma ponderada
dos caracteres: uma maneira de o fazer é, por exemplo:

DCC405-Estrutura de Dados II | Tabela Hash 24/37


Tabela Hash – Função de Hash
● Na literatura é sempre referido que que devemos recorrer a "valores auxiliares"
primos, já que estes reduzem bastante o risco de colisão.
● Também podemos recalcular a base a cada iteração do loop principal, de modo a
evitar anomalias com chaves altamente regulares:

DCC405-Estrutura de Dados II | Tabela Hash 25/37


Colisões

DCC405-Estrutura de Dados II | Tabela Hash 26/37


Colisões em tabelas de hash
● Como as funções hash mapeiam chaves de tamanho variável para índices de tamanho fixo, elas
mapeiam um conjunto infinito para um finito. Dessa forma, as colisões eventualmente ocorrerão.
● Em tabelas de hash, uma colisão significa que a função de hash mapeou várias chaves necessárias para
o mesmo índice e, consequentemente, para o mesmo bucket de memória da tabela.

DCC405-Estrutura de Dados II | Tabela Hash 27/37


Colisão: Resolução via Encadeamento Externo (Separate
Chaining)
● Aqui, cada posição da tabela tem um ponteiro
para uma lista: desta forma, as colisões são
resolvidas juntando o novo elemento ao início
da lista. As remoções são realizadas removendo
o elemento da lista. Aqui, o comprimento médio
das listas será
● Apesar de o pior caso ser linear - acontece caso
todos os elementos tenham o mesmo índice
associado - este pior caso tem probabilidade
baixíssima de ocorrer, pelo que em média
mantêm-se tempos de acesso "baratos".

DCC405-Estrutura de Dados II | Tabela Hash 28/37


Colisão: Resolução via Sondagem Linear (Linear Probing)
● Cada posição da tabela guarda no máximo um elemento. Se
calcularmos um índice mas nesta posição já existir um
elemento, devemos continuar a mover-nos pela tabela até o
final, até encontrar um espaço livre (inserindo-o nesse
espaço). A procura e remoção de elementos funcionam todas
desta maneira: calcula-se o índice e verifica-se se o elemento
está lá; caso não esteja, continuamos a avançar pela tabela
até encontrar uma posição vazia (caso encontremos uma
posição vazia antes de encontrar o elemento pretendido, o
elemento não está na tabela). No caso da remoção de um
elemento, após remover o elemento, devemos reorganizar a
tabela de forma a eliminar este novo espaço vazio:

DCC405-Estrutura de Dados II | Tabela Hash 29/37


Colisão: Resolução via Sondagem Linear (Linear Probing)
● Avançamos pela tabela até encontrar um elemento cujo módulo é igual ao índice do
elemento a remover, e colocamos esse elemento no lugar do que removemos. De seguida,
chegamos todos os elementos que estão à sua frente "uma casa para trás".
● O exemplo seguinte exemplifica isso.
Removemos 14 da posição 1, e 14
módulo 7 é igual a 0. Vamos, portanto,
pesquisar pela tabela até encontrar um
elemento cujo módulo 7 é também igual
a 0. Ao encontrarmos esse elemento, 70,
colocamos ele na posição 1, e "chegamos
para trás" os elementos à sua frente -
neste caso, 20.

DCC405-Estrutura de Dados II | Tabela Hash 30/37


Colisão: Resolução via Sondagem Linear (Linear Probing)
● A estratégia em questão requer que se saiba o número de elementos a inserir à
priori! Caso contrário, podíamos ficar sem espaço para inserir elementos na
tabela. Mais ainda, a= MN tem de ser idealmente menor que 11, já que o número
de operações necessárias para encontrar um elemento na tabela é dado por:

Fator de Carga

DCC405-Estrutura de Dados II | Tabela Hash 31/37


FATOR DE CARGA / LOAD FACTOR
Uma estatística muito interessante sobre tabelas Hash é o fator de carga. Ele permite verificar a proporção de
distribuição das chaves dentro do array/tabela. Para tal utilizamos:

carga = n / k

Onde n é o número de chaves inseridas e k é o número de espaços disponíveis no array/tabela como um todo (total de
endereços). Os valores do fator de carga variam entre 0.0 e 1.0.

Quanto maior o fator de carga, menor a chance de o tempo de execução ser constante O(1). Ou seja, quanto menos
espaços disponíveis dentro da tabela, maior a chance de colisão para novas chaves.

É muito comum estabelecer um limite superior ao qual o fator de carga sempre deve estar abaixo. Cada
implementação de tabelas Hash pode escolher sua estratégia. Mas um exemplo seria ao chegar em um fator de carga
igual a 0.8 se faz uma realocação da tabela de modo a aumentar o valor de k, ou seja, o número total de espaços
disponíveis (comumente chamados de buckets).

DCC405-Estrutura de Dados II | Tabela Hash 32/37


Colisão: Resolução via Double Hashing
● Tal como na sondagem linear, vamos sempre
procurar guardar elementos na tabela em si,
sem recorrer a estruturas auxiliares (como
listas ou similares). Vamos, contudo, utilizar
outra técnica para resolver conflitos: em vez
de procurar sequencialmente a próxima
posição vazia, vamos utilizar uma segunda
hash function para determinar o incremento
ao índice originalmente obtido. Este
incremento deverá ser sempre primo
relativamente ao tamanho da tabela.

DCC405-Estrutura de Dados II | Tabela Hash 33/37


Colisão: Resolução via Double Hashing

DCC405-Estrutura de Dados II | Tabela Hash 34/37


Tabelas de Dispersão Dinâmicas
● Para evitar tempos incomportáveis de inserção, remoção e procura de
elementos, é costume utilizar-se tabelas de dispersão dinâmicas: assim
que a tabela estiver meio cheia, duplicamos o seu tamanho. Esta
duplicação não é uma operação barata - temos de fazer re-hash dos
nossos elementos para os colocar na nova tabela. É, contudo, uma
operação pouco frequente (isto considerando que inicializámos a
tabela com um tamanho conservador), pelo que os ganhos acabam
por superar essa perda.

DCC405-Estrutura de Dados II | Tabela Hash 35/37


Resumo sobre tratamento de colisões

● A sondagem linear é a estratégia mais rápida, quando temos tabelas


esparsas;
● O double hashing acaba por ser o melhor custo benefício tempo/memória,
já que não só não utilizamos estruturas de dados auxiliares como não
procuramos elementos sequencialmente;
● O encadeamento externo é o mais fácil de implementar, sofrendo contudo
aumentos graduais de desempenho e acabando por ocupar bastante memória.

DCC405-Estrutura de Dados II | Tabela Hash 36/37


Implementação

Let’s code </>...

Ver:
https://realpython.com/python-hash-table/
https://www.journaldev.com/35238/hash-table-in-c-plus-plus

DCC405-Estrutura de Dados II | Tabela Hash 37/37

Você também pode gostar