Tabelas Hash,encadeamento separado
Contato
- Jean Paulo Martins (jeanmartins utfpr edu br)
- Sala 105, Bloco S (UTFPR - Campus Pato Branco)
Conteúdo
Estruturas de dados usando C (Tenenbaum, Sec.7.4, pg. 595 - 603; 620 - 628)
Contextualização
Uma tabela hash armazena informações que possam ser identificadas por uma chave única. Suponhamos, por exemplo, uma estrutura que armazene informações de um aluno, e o identifique por um número de matrícula.
typedef struct
{
long matricula;
char nome[100];
struct _item* prox;
struct _item* ant;
} Aluno;
Caso tivéssemos um número fixo de alunos N
, as respectivas estruturas podem ser armazenadas em um vetor de tamanho N
.
const int N = 100;
Aluno turma[N]; // Uma turma tem, neste exemplo, um máximo de 100 alunos
Nestas condições, considere a seguinte problema:
- Dada o vetor
turma
contendoN
alunos, retorne o aluno de matrículaM
!
A matrícula como índice do vetor turma
Se assumirmos que o campo matricula
da estrutura Aluno
é um número 0…N−1; o vetor turma pode ser criado de forma que o Aluno
de matrícula i (0≤i<N), esteja na posição i do vetor turma
.
Neste caso, para retornar o aluno de matrícula 0≤M<N, bastaria retornar o aluno armazenado na respectiva posição.
// Caso o índice do vetor equivalha ao número de matrícula.
return turma[M];
A matrícula não pode ser usada como índice do vetor turma
Considere agora, que o campo matricula
é um número muito maior, no intervalo 0≤m<107. Devido ao valor máximo ser muito grande, não é viável criar um vetor turma[10 000 000]
, para armazenar apenas N alunos.
Procurar o aluno de matrícula M em uma list
Caso utilizássemos uma lista para armazenar todos os N alunos, a procura pelo aluno de matrícula M
envolveria o percurso dessa lista do início ao fim, comparando-se a matrícula dos alunos encontrados com M
até que o aluno de matrícula M
seja encontrado. Existiria outra estrutura de dados em que a busca pelo aluno de matrícula M
possa ser feita de forma mais eficiente?
Procurar o aluno de matrícula M em uma tabela hash
Uma alternativa para o armazenamento dos N
alunos é a utilização de uma tabela hash, em que o número de matrícula
seria utilizado como chave de acesso ao respectivo aluno.
Veja Seção 7.4 pgs. (595-598)) para uma leitura introdutória à tabelas hash.
Considere que a capacidade (n) do vetor utilizado como tabela hash será sempre menor que o número total de alunos, ou seja, n<N.
Conflitos de espalhamento
Conflitos de espalhamento deverão ser tratados por meio da técnica de encadeamento separadoSeção 7.4 pgs. (624-628) , a qual consiste em armazenar em uma lista, todos alunos cujos indices caírem sobre o mesmo valor de hash.
Estrutura da tabela
A tabela hash será, neste caso, um vetor de listas de alunos, isto é, em cada uma de suas n
posições, o vetor conterá uma Lista*
cujos itens armazenarão uma estrutura Aluno*
cada. Uma tabela hash de capacidade 3, inicialmente, assume a configuração a seguir:
0 -> NULL
1 -> NULL
2 -> NULL
Após a inserção dos valores a seguir
1923123123 Jean-Paulo-Martins
1231231249 Maria-Josefina
1449989959 Joaquim-Felix
E uma função de espalhamento, definida para uma tabela de tamanho n=3;
int h(long mat) {
return mat % n;
}
A tabela hash correspondente teria a seguinte estrutura:
Posição | Itens da lista
0 |-> {1923123123, "Jean-Paulo-Martins"} -> NULL
1 |-> {1231231249, "Maria-Josefina"} <-> {1449989959, "Joaquim-Felix"}-> NULL
2 |-> NULL
Observe que o conflito de espalhamento foi solucionado armazenando-se todos os alunos com o mesmo hash h(matricula)
na mesma lista, na posição h(matricula)
da tabela.
Objetivos
Implementar uma função que permita a inserção de N alunos em uma tabela hash de capacidade n, com n<N. Com tratamento de conflitos de espalhamento por encadeamento separado.
Entrada de dados
O primeiro item de entrada é o tamanho máximo, ou capacidade, da tabela hash a ser criada. A leitura desse valor é efetuada da seguinte forma em C++:
int capacidade;
cin >> capacidade;
O próximo número de entrada, indicará a quantidade de elementos a serem inseridos na tabela hash.
int n; // Número de alunos a serem inseridos
cin >> n;
Definidas a capacidade e a quantidade de alunos, os próximos itens a serem lidos descreverão os alunos, que deverão ser amazenados em struct Aluno
. Portanto, para lermos um aluno.
long matricula;
char[100] nome;
cin >> matricula >> nome;
Assumindo-se que a função que insere um aluno na tabela hash já esteja implementada, e obedeça o seguinte protótipo de função:
int inserir(Lista** thash, long matricula, char* nome);
A entrada de dados completa pode ser implementada da seguinte forma:
int capacidade;
int n;
cin >> capacidade >> n;
Lista** thash = (Lista**) malloc(capacidade * sizeof(Lista*));
long matricula;
char[100] nome;
for (int i = 0; i < n; i++) {
cin >> matricula >> nome;
inserir(thash, matricula, nome);
}
Exemplo de entrada com 3 alunos e capacidade 3
3 3
1923123123 Jean-Paulo-Martins
1231231249 Maria-Josefina
1449989959 Joaquim-Felix
Exemplo de saída
Imprimir a tabela hash, cada linha a lista correspondente. No exemplo a seguir:
Posição | Itens da lista
0 |-> {1923123123, "Jean-Paulo-Martins"} -> NULL
1 |-> {1231231249, "Maria-Josefina"} <-> {1449989959, "Joaquim-Felix"}-> NULL
2 |-> NULL
Deve-se imprimir (a terceira linha está vazia):
0:1923123123
1:1231231249, 1449989959
2: