Você está visualizando atualmente Tabela Hash

Tabela Hash

Como tantas outras, uma Tabela Hash é mais uma estrutura de dados, e assim como toda estrutura de dados, possui um objetivo bem específico. O uso de uma tabela hash é indicado quando se sabe o tamanho do conjunto de dados e é necessário uma grande quantidade de busca que deve ser respondida rapidamente.

Em sua forma mais simples, uma tabela hash é um vetor maior que o tamanho do conjunto de dados que irá armazenar. Sua eficiência ou não no processo de busca está diretamente atrelado à função de espalhamento. É esta função que recebe uma chave (um nome, um cpf, uma matrícula por exemplo) e gera o índice no vetor onde o dado será salvo.

No processo de busca, a chave buscada passa pela mesma função de espalhamento, gerando assim o mesmo índice, tornando a busca localizada em uma região da tabela.

Toda função de espalhamento, por melhor que seja, pode gerar colisões, quando a função gera o mesmo índice para dados diferentes. Quando isso ocorre, há ao menos duas formas para seu tratamento.

A primeira, é inserir o novo elemento na próxima posição vazia na tabela, como apresentado na figura a seguir. Neste exemplo, o conjunto de dados possui 6 elementos. O tamanho M da tabela é 11 (primo mais próximo do dobro do tamanho do conjunto de dados) e a função de espalhamento utilizada é o resto da divisão do número a ser inserido pelo tamanho da tabela (chave % M).

Ao inserir o sexto elemento (40), a função de espalhamento gera o índice 7 (resto de 40 dividido por 11), que já está ocupado pelo elemento 73. Temos aí uma colisão. A próxima posição, 8, também está preenchida (8), fazendo com que o 40 seja inserido na posição 9.

Tabela Hash linear

A segunda forma para tratar uma colisão é criar um vetor de listas encadeadas. Assim, cada posição do vetor terá uma lista. Ao fazer uma busca, não é necessário buscar em toda a tabela, mas apenas na lista do índice gerado pela função de espalhamento, como apresentado a seguir.

Tabela Hash com listas encadeadas

No vídeo a seguir explico em mais detalhes como funciona uma Tabela Hash.

Neste próximo vídeo apresento uma possível implementação de uma Tabela Hash linear com a linguagem de programação C. O código encontra-se abaixo do vídeo.

Código Tabela Hash linear

#include <stdio.h>
#include <stdlib.h>

// constante repesenta o tamanho da tabela
#define M 19

// estrutura Pessoa com nome e uma matrícula
typedef struct{
    int matricula;
    char nome[50];
}Pessoa;

// nossa tabela hash do tipo Pessoa
Pessoa tabelaHash[M];

// inicializa nossa tabela com o valor de codigo -1
void inicializarTabela(){
    int i;
    for(i = 0; i < M; i++)
        tabelaHash[i].matricula = -1;
}

// função de espalhamento (resto da divisão da chave por M)
int gerarCodigoHash(int chave){
    return chave % M;
}

// função para ler e retornar uma pessoa
Pessoa lerPessoa(){
    Pessoa p;
    printf("Digite a matricula: ");
    scanf("%d", &p.matricula);
    scanf("%*c");
    printf("Digite o nome: ");
    fgets(p.nome, 50, stdin);
    return p;
}

// inserir uma pessoa na tabela
void inserir(){
    Pessoa pes = lerPessoa();
    int indice = gerarCodigoHash(pes.matricula);
    while(tabelaHash[indice].matricula != -1)
        indice = gerarCodigoHash(indice + 1);
    tabelaHash[indice] = pes;
}

Pessoa* buscar(int chave){
    int indice = gerarCodigoHash(chave);
    while(tabelaHash[indice].matricula != -1){
        if(tabelaHash[indice].matricula == chave)
            return &tabelaHash[indice];
        else
            indice = gerarCodigoHash(indice + 1);
    }
    return NULL;
}

void imprimir(){
    int i;
    printf("\n------------------------TABELA---------------------------\n");
    for(i = 0; i < M; i++){
        if(tabelaHash[i].matricula != -1)
            printf("%2d = %3d \t %s", i, tabelaHash[i].matricula, tabelaHash[i].nome);
        else
            printf("%2d =\n", i);
    }
    printf("\n----------------------------------------------------------\n");
}

int main() {
    int op, chave;
    Pessoa *p;

    inicializarTabela();

    do{
        printf("1 - Inserir\n2 - Buscar\n3 - Imprimir\n0 - Sair\n");
        scanf("%d", &op);

        switch(op){
        case 0:
            printf("Saindo...\n");
            break;
        case 1:
            inserir();
            break;
        case 2:
            printf("Digite a matricula a ser buscada: ");
            scanf("%d", &chave);
            p = buscar(chave);

            if(p)
                printf("\n\tMatricula: %d \tNome: %s\n", p->matricula, p->nome);
            else
                printf("\nMatricula nao encontrada!\n");
            break;
        case 3:
            imprimir();
            break;
        default:
            printf("Opcao invalida!\n");
        }

    }while(op != 0);

    return 0;
}

Por fim, um exemplo de Tabela Hash com listas encadeadas, também com a linguagem de programação C. O código encontra-se após o vídeo.

Código Tabela Hash com listas encadeadas

#include <stdio.h>
#include <stdlib.h>

// tamanho da tabela
#define M 19

// tipo pessoa
typedef struct {
    int matricula;
    char nome[50];
} Pessoa;

// tipo nó usado na lista encadeada
typedef struct no {
    Pessoa pessoa;
    struct no *proximo;
} No;

// tipo lista com um ponteiro para o primeiro nó
typedef struct {
    No *inicio;
    int tam;
} Lista;

// nossa tabela (vetor de ponteiros para listas)
Lista *tabela[M];

//--------------------------------- fim definições variáveis --------------------

//--------------------------------- funções meus tipos --------------------------

// cria e retorna um tipo pessoa
Pessoa criarPessoa() {
    Pessoa p;
    printf("Digite o nome da pessoa: ");
    scanf("%*c");
    fgets(p.nome, 50, stdin);
    printf("Digite a matricula: ");
    scanf("%d", &p.matricula);
    return p;
}

// imprime uma pessoa
void imprimirPessoa(Pessoa p) {
    printf("\tNome: %s\tMatricula: %d\n", p.nome, p.matricula);
}

//-------------------------------- início funções lista -------------------------
// cria uma lista vazia e retorna seu endereço na memória
Lista* criarLista() {
    Lista *l = malloc(sizeof(Lista));
    l->inicio = NULL;
    l->tam = 0;
    return l;
}

/*
    inserir no início da lista
    PARÂMETROS
    p - nova pessoa a ser inserida
    *lista - endereço de uma lista encadeada.
*/
void inserirInicio(Pessoa p, Lista *lista) {
    No *no = malloc(sizeof(No));
    no->pessoa = p;
    no->proximo = lista->inicio;
    lista->inicio = no;
    lista->tam++;
}

// busca um elemento na lista
No* buscarNo(int mat, No *inicio) {

    while(inicio != NULL) {
        if(inicio->pessoa.matricula == mat)
            return inicio;
        else
            inicio = inicio->proximo;
    }
    return NULL;// matricula não encontrada
}

void imprimirLista(No *inicio) {
    while(inicio != NULL) {
        imprimirPessoa(inicio->pessoa);
        inicio = inicio->proximo;
    }
}
//---------------------------------- fim funções lista -------------------------

//--------------------------- início funções tabela hash -----------------------
// inicializa a tabela com uma lista vazia em cada posição do vetor
void inicializar(){
    int i;
    for(i = 0; i < M; i++)
        tabela[i] = criarLista();
}

// função de espalhamento
int funcaoEspalhamento(int mat){
    return mat % M;
}

// cria uma pessoa e a insere na tabela
void inserTabela(){
    Pessoa pes = criarPessoa();
    int indice = funcaoEspalhamento(pes.matricula);
    inserirInicio(pes, tabela[indice]);
}

// busca uma pessoa. Seu retorno é um endereço ou NULL
Pessoa* buscarPessoaTabela(int mat){
    int indice = funcaoEspalhamento(mat);
    No *no = buscarNo(mat, tabela[indice]->inicio);
    if(no)
        return &no->pessoa;
    else
        return NULL;
}

// imprimir tabela
void imprimirTabela(){
    int i;
    printf("\n---------------------TABELA-------------------------\n");
    for(i = 0; i < M; i++){
        printf("%d Lista tamanho: %d\n", i, tabela[i]->tam);
        imprimirLista(tabela[i]->inicio);
    }
    printf("---------------------FIM TABELA-----------------------\n");
}

int main() {
    int op, mat;
    Pessoa *p;

    inicializar();

    do {
        printf("\n0 - Sair\n1 - Inserir\n2 - Buscar\n3 - Imprimir tabela\n");
        scanf("%d", &op);
        switch(op) {
        case 0:
            printf("saindo...\n");
            break;
        case 1:
            inserTabela();
            break;
        case 2:
            printf("Qual a matricula a ser buscada? ");
            scanf("%d", &mat);
            p = buscarPessoaTabela(mat);
            if(p) {
                printf("Pessoa encontrada: Matricula: %d\tNome: %s", p->matricula, p->nome);
            } else
                printf("Pessoa nao contrada!\n");
            break;
        case 3:
            imprimirTabela();
            break;
        default:
            printf("Opcao invalida!\n");
        }
    } while(op != 0);

    return 0;
}

Deixe um comentário

1 × 3 =

Wagner Gaspar

Capixaba de São Gabriel da Palha, Espírito Santo. Bacharel em Ciência da Computação pela Universidade Federal do Amazonas e mestre em informática pela Universidade Federal do Espírito Santo.