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\dots N-1$; o vetor turma pode ser criado de forma que o Aluno
de matrícula $i$ ($0 \leq i < N$), esteja na posição $i$ do vetor turma
.
Neste caso, para retornar o aluno de matrícula $0 \leq 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 \leq m < 10^7$. 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: