O acesso aos elementos é aleatório, isto é, qualquer elemento numa colecção pode ser acedido realizando uma pesquisa pela chave.

Documentos relacionados
DICIONÁRIOS. template<class K,class T> class Par { public: K chave; T valor; Par():chave(),valor()

Algoritmos de pesquisa. Tabelas de dispersão/hash

TABELAS DE DISPERSÃO/HASH

Hashing: conceitos. Hashing

Tabelas Hash. informação, a partir do conhecimento de sua chave. Hashing é uma maneira de organizar dados que:

Tabelas de dispersão/hash

Matrizes esparsas: definição

Tabelas Hash. Prof. Túlio Toffolo BCC202 Aulas 23 e 24 Algoritmos e Estruturas de Dados I

Métodos de Busca Parte 2

i a[i]

Tabelas de Dispersão - Introdução (1)

Endereçamento Aberto

Matemática Discreta 12

Bases de Dados. Remoções em árvores B + Remoção em árvores B +

HASHING HASHING 6/10/2008

AED Algoritmos e Estruturas de Dados LEEC /2007. Tabelas de Dispersão

SCC0601 Introdução à Ciência da Computação II. Prof. Lucas Antiqueira

Tabelas Hash O Que é uma Tabela Hash? O Que é uma Tabela Hash? O Que é uma Tabela Hash? Inserindo um Novo Registro. O Que é uma Tabela Hash?

Métodos de Busca. Parte 2. ICC2 Prof. Thiago A. S. Pardo. Baseado no material do Prof. Rudinei Goularte

Introdução Métodos de Busca Parte 2 - Hashing

Tabelas de hash Acabamos de estudar como implementar uma tabela hashing aberta e estudaremos agora como implementar uma tabela hashing fechada ou

Estruturas de Informação Árvores B ÁRVORES B

Tabela Hash. Disciplina de Algoritmos e Estrutura de Dados III. Prof. Marcos Antonio Schreiner 15/05/2015

Hashing Externo. SCC-503 Algoritmos e Estruturas de Dados II. Thiago A. S. Pardo M.C.F. de Oliveira Cristina Ciferri

MC3305 Algoritmos e Estruturas de Dados II. Aula 02 Hashing. Prof. Jesús P. Mena-Chalco.

TAD dicionário. Métodos do TAD dicionário:

Hashing convencional...

Árvores B. Prof. Márcio Bueno. / Fonte: Material da Prof a Ana Eliza Lopes Moura

TABELA HASH. Prof. André Backes. Princípio de funcionamento dos métodos de busca

UNIVERSIDADE DE SÃO PAULO INSTITUTO DE CIÊNCIAS MATEMÁTICAS E DE COMPUTAÇÃO

Hashing. Hashing. Hashing versus Indexação. Hashing. Hashing convencional... Exemplo de espalhamento. Revisão...

Estruturas de Dados Tabelas de Espalhamento

13 Hashing (parte 2) SCC201/501 - Introdução à Ciência de Computação II

Bases de Dados. Índices. Discos. transferência rápida e aos bytes. transferência lenta e em blocos (512B ~ 4KB) memória. disco BD / aplicação

Implemente a função de remoção de uma chave na tabela hashing fechada em questão. void remover(tabela tabela, int n) {

HASHING Hashing Motivação - Acesso Direto:

TABELAS HASH. Vanessa Braganholo Estruturas de Dados e Seus Algoritmos

Tabela Hash. Prof. Msc. Mariella Berger

Pesquisa em Memória Primária Hashing

Tabelas de Dispersão. Estrutura de Dados e Algoritmos

Transformação de Chave. (Hashing)

Proposta de trabalho

Bases de Dados. Índices. Discos. transferência lenta. transferência rápida e em blocos (512B ~ 4KB) e aos bytes. memória.

Universidade Estadual do Oeste do Parana - UNIOESTE Jhonata R.de Peder Marcelo Schuck

Dicionários. Prof. César Melo

Hashing externo (II) Graça Nunes. Fonte: Folk & Zoelick, File Structures

ACH2024. Aula 22 Hashing Externo - Hashing estático e dinâmico (extensível) Prof Helton Hideraldo Bíscaro

Hashing Endereçamento Direto Tabelas Hash

Transformação de Chave - Hashing

UNIVERSIDADE DA BEIRA INTERIOR

Pesquisa em memória primária: hashing. Algoritmos e Estruturas de Dados II

Tabelas de Hash MBB. Novembro de Algoritmos e Complexidade LEI-LCC

Edital de Seleção 055/2017 PROPESP/UFAM. Prova de Conhecimento. Caderno de Questões

Algoritmos e Estruturas de Dados: Tabela de Dispersão com Encadeamento

Pesquisa em Memória Primária. Prof. Jonas Potros

Tabelas de Dispersão. Algoritmos e Estruturas de Dados Verão Cátia Vaz 1

Vamos considerar um arquivo de dados que armazena uma lista de alunos. Cada registro é um objeto com um número de matrícula e um nome.

Programação: Vetores

Hashing. Cormen Capítulo 11

Hashing (Tabela de Dispersão)

Estruturas de dados e algoritmos fundamentais

Listas (Parte 1) Túlio Toffolo BCC202 Aula 09 Algoritmos e Estruturas de Dados I

Hashing. ACH Introdução à Ciência da Computação II. Delano M. Beder

1/36 LISTAS. Programação II

Pesquisa: operação elementar

Tabelas Hash. Prof. Túlio Toffolo BCC202 Aula 21 Algoritmos e Estruturas de Dados I

Edital de Seleção 024/2017 PROPESP/UFAM. Prova de Conhecimento. Caderno de Questões

Dicionários. TAD Orientado a conteúdo

Tabelas de Espalhamento (hash)

Universidade Estadual de Mato Grosso do Sul Bacharelado em Ciência da Computação Algoritmos e Estruturas de Dados II Prof. Fabrício Sérgio de Paula

UNIVERSIDADE DA BEIRA INTERIOR

A inserção da chave 6 da árvore acima resulta na árvore abaixo.

Árvores. Thiago Martins, Fabio Gagliardi Cozman. PMR2300 / PMR3201 Escola Politécnica da Universidade de São Paulo

Estruturas de Dados Estruturas de Dados Fundamentais

Tabelas de símbolos e de distribuição AULA 23. Tabela de símbolos. Um exemplo simples. Interface

AED2 - Aula 01 Apresentação, estruturas de dados, tabelas de símbolos e hash tables

Hashing Organização Direta de Arquivos

Edital de Seleção 023/2018 PROPESP/UFAM. Prova de Conhecimento. Caderno de Questões

Árvores. Thiago Martins, Fabio Gagliardi Cozman. PMR2300 / PMR3201 Escola Politécnica da Universidade de São Paulo

Complexidade de Algoritmos

UNIVERSIDADE DA BEIRA INTERIOR

Algoritmos e Programação

indexação e hashing Construção de Índices e Funções Hash Diego Gomes Tomé - MSc. Informática Orientador: Prof. Dr. Eduardo Almeida October 13, 2016

Árvores B. Hashing. Estrutura de Dados II Jairo Francisco de Souza

Listas Estáticas. SCC Algoritmos e Estruturas de Dados I. Prof. Fernando V. Paulovich. *Baseado no material do Prof.

8. Árvores. Fernando Silva DCC-FCUP. Estruturas de Dados. Fernando Silva (DCC-FCUP) 8. Árvores Estruturas de Dados 1 / 38

8. Árvores. Fernando Silva. Estruturas de Dados DCC-FCUP. Fernando Silva (DCC-FCUP) 8. Árvores Estruturas de Dados 1 / 38

Algoritmos e Estrutura de Dados. Aula 11 Estrutura de Dados: Tabelas Hash Parte II Prof. Tiago A. E. Ferreira

Listas Lineares Ordenadas

Árvores. Fabio Gagliardi Cozman. PMR2300 Escola Politécnica da Universidade de São Paulo

Hashing. Prof. Josenildo Silva IFMA 2014

Estruturas de dados para listas arrays e listas ligadas

Dois parâmetros essenciais t T tempo de transferência de um bloco. assume-se igual para operações de leitura e escrita

ÁRVORE B. Vanessa Braganholo Estruturas de Dados e Seus Algoritmos

ESTRUTURA DE DADOS E ALGORITMOS. Árvores Binárias de Busca. Cristina Boeres

ACH2025. Laboratório de Bases de Dados Aula 8. Indexação e Hashing Parte 1. Professora: Fátima L. S. Nunes SISTEMAS DE INFORMAÇÃO

Algoritmos e Estruturas de Dados 2005/2006. Algoritmo: conjunto claramente especificado de instruções a seguir para resolver um problema

Transcrição:

DICIONÁRIOS São assim designadas as colecções de elementos em que cada elemento tem um campo chamado chave e não existem valores de chaves repetidos. As operações características que permitem a sua manipulação são: Juntar elemento, dada a chave Pesquisar elemento por chave Eliminar elemento com uma dada chave. O acesso aos elementos é aleatório, isto é, qualquer elemento numa colecção pode ser acedido realizando uma pesquisa pela chave. No caso de dicionários com chaves duplicadas é necessário uma regra para retirar a ambiguidade na pesquisa, no eliminar elementos. Como exemplo de aplicação de dicionários podemos citar as tabelas de símbolos nos compiladores (dicionários com chaves duplicadas). Nesta aplicação, a chave é o identificador e a estrutura conterá ainda o tipo, (int, float,...), endereço de memória e valor. Geralmente a pesquisa devolve o elemento criado mais recentemente. As representações vulgares para dicionários são listas ligadas ordenadas (será deixado como exercício o criar a classe Lista Ordenada), ou vectores ordenados ou ainda através de tabelas de Hash, que estudaremos a seguir. TABELAS DE HASH Consideremos o caso ideal: um elemento elem, tem a chave k e existe uma função f (função de hash), esta função vai mapear o elemento elem (chave k ) na posição da tabela, f(k), onde se deve encontrar o elemento elem com chave k. Exemplo: suponhamos uma escola em que os números dos alunos variam entre 991000 e 992000. A função f deverá ser tal que fará um mapeamento entre o número do aluno e a posição na tabela e poderá ser esta: f(k)=k - 991000 Assim o aluno n 0 991346 (k=991346), ocupará a posição (posição=f(k) ),346 (=k- 99 1000) da tabela. Instituto Superior de Engenharia do Porto 1

De uma forma geral a tabela é inicializada a 0 e a inicialização será de ordem n, (n é o número de elementos possíveis da tabela) e a pesquisa de ordem 1, assim como o juntar elemento e o eliminar. Acontece a maioria das vezes que o número de elementos possíveis para incluir na tabela é muito superior ao tamanho da tabela e neste caso é possível que a mesma função aplicada a chaves diferentes dê como resultado o mesmo valor de índice da tabela. Cada posição na tabela de hash designa-se por bucket. Cada bucket pode conter um ou mais elementos. No caso de a aplicação da função de hash a duas chaves distintas, do mesmo universo resultar na mesma posição e se o bucket estiver completo diremos que houve colisão. Para simplificar consideramos que cada bucket contem um elemento. As funções de hash terão que ser definidas criteriosamente. Uma função standard, para o caso de chaves inteiras será fazer o resto da divisão inteira da chave pelo tamanho da tabela. Claro que isto será uma opção razoável se a chave não tiver propriedades indesejáveis, como o exemplo que apontamos a seguir: Exemplo: suponhamos uma tabela com 10 posições, e a função de hash a standard. Se todas as chaves terminassem em 0, então seria muito má escolha esta função uma vez que todos os valores colidiriam na posição 0. Também o tamanho da tabela não estaria bem definido. É aceitável que o tamanho da tabela seja um número primo. Quando a entrada são valores aleatórios inteiros, então a função standard é muito simples e distribui as chaves igualmente. Se a chave for uma string ( podemos usar a seguinte função de hash int hash (char chave[ ], const int tamanho) char *ptr=chave; int valor=0; while( *ptr) valor = valor + (*ptr ++); return (valor % tamanho); Esta função é fácil de implementar, mas se a tabela é grande não distribui bem as chaves. Suponhamos que o tamanho da tabela é 10007 (número primo) e que as chaves contêm 8 ou menos caracteres. Como um caracter é um inteiro Instituto Superior de Engenharia do Porto 2

entre 0 e 127 a função só assume valores entre 0 e 1016 (127*8). Isto é evidentemente, uma distribuição pouco equitativa. Outra possível função seria: int hash (char chave[ ], const int tamanho) return (chave[o] + 26*chave[1] + 676*chave[2]) % tamanho; Assume-se que a chave tem pelo menos comprimento 3. 26 será o nº de letras do alfabeto e 676 será 26 2. Esta função só examina os primeiros 3 caracteres e se o tamanho da tabela for como o indicado anteriormente,10007, então pode esperar-se uma distribuição razoavelmente equitativa. Mas, infelizmente as linguagens não são aleatórias, embora haja 26 3 = 17576 combinações possíveis de 3 caracteres só se verificam 2851 combinações. Mesmo que essas não colidam só 28% da tabela será usada. Portanto esta função também não será apropriada se a tabela for muito grande. Podemos ainda usar outra função: int hash (char chave[ ], const int tamanho) char *ptr=chave; int valor=0; while( *ptr) valor = valor <<5 + (*ptr ++); valor<<5 equivale a multiplicar por 32 return (valor % tamanho); Usou-se o valor 32, para se poder fazer um shift de bits. Esta função é rápida, excepto se as chaves forem muito grandes. É prática corrente não usar todos os caracteres da chave, mas usar só alguns, por exemplo os das posições ímpares MÉTODOS PARA RESOLUÇÃO DE COLISÕES 1. Encadeamento Externo Quando ocorre uma colisão, os elementos que colidiram são colocados numa lista ligada. No exemplo abaixo esquematizado, a tabela de hash está organizada de forma Instituto Superior de Engenharia do Porto 3

a que cada bucket contem um apontador para um nó (cabeça da lista de colisões). 11 22 44 58 80 Tabela[ ] 16 38 60 93 A tabela representada tem 11 posições, e a função de hash escolhida é a função da divisão, que é a mais vulgar. Estamos a supor que a chave é um valor inteiro. A função toma o seguinte aspecto: f(k) = k % D, em que o índice da tabela é dado pelo resto da divisão inteira da chave pelo tamanho, D, da tabela. As posições na tabela são portanto numeradas de 0 a D- 1. Há muitas vezes necessidade de pré processar a chave antes de lhe aplicar a função de hash. Assim para aplicação da função divisão há que converter a chave num número inteiro. Os algoritmos de pesquisa, juntar elemento e eliminar elemento traduzem-se nos correspondentes algoritmos em lista ordenada. A cabeça é dada pelo valor do apontador na posição da tabela dada por h(k). Poder-se-ia melhorar a performance das lista se considerássemos por exemplo, além do apontador cabeça um apontador para a cauda da lista, verificando-se no inicio das operações se o elemento que colidiu é o que apresenta a maior chave. Outra forma de melhorar a performance seria considerar, em vez de lista ordenada, uma estrutura de árvore binária de pesquisa, mas isto deixamos para exercício. Instituto Superior de Engenharia do Porto 4

2. Endereçamento Linear Aberto Este método também se usa quando o tamanho da tabela é menor do que a gama de valores possíveis resultantes da aplicação da função de hash e que por consequência surgem colisões. Assim sempre que se verifica uma colisão, não é possível colocar o elemento na posição fornecida pela função de hash uma vez que o bucket correspondente está cheio, recorre-se a este método, obrigando a colocar o elemento no próximo bucket vazio. Desta forma a pesquisa para o próximo bucket disponível é feita considerando a tabela como se fosse circular. A pesquisa inicia-se no bucket dado pela função de hash e um dos 3 casos pode acontecer: Encontramos um bucket com a chave que procurávamos (encontramos o elemento a pesquisar, pesquisa com sucesso) Encontramos um bucket vazio (pesquisa sem sucesso, o elemento não existe) Regressamos ao bucket inicial (pesquisa sem sucesso e a tabela está cheia). Vamos supor que tínhamos os mesmos valores usados na exemplificação do encadeamento externo e cuja entrada era dada pela ordem seguinte: 11, 58, 38, 60, 93, 22, 44, 16, 80 11 22 44 58 80 38 60 93 16 0 1 2 3 4 5 6 7 8 9 10 A posição para a chave 11 tem índice 0. Quando colocar o valor 22, que também deveria ocupar a posição 0 esta já estará ocupada. Teremos então que percorrer a lista fazendo: posição =( posição + 1 )% D até encontrar uma posição vazia, que felizmente neste caso será a próxima. O valor de D neste exemplo tem o valor 11. Seguidamente descreveremos o correspondente algoritmo para pesquisar e inserir o valor de chave x. Consideramos que a nossa tabela terá um campo conteúdo e um campo designado por ocupação que será verdadeiro ou falso conforme a tabela tem esse índice ocupado ou não. Pesquisar_Inserir(x) Inicio Pesquisar Instituto Superior de Engenharia do Porto 5

j = h(x) inicial = j enc = 0 Repete Se (tabel[j].ocupaçao=verdade e tabela[j].conteudo=x) Então enc=1 Senão j=(j+1)% D Até (enc=1 ou j=inicial ou tabela[j].ocupaçao=falso) Inserir Se (enc=0) Então Se (j=inicial) Então Overflow Senão tabela[j].conteudo=x tabela[j]. Ocupaçao=verdade Senão O elemento já existe Fim Seguidamente analisaremos um algoritmo para eliminarmos um elemento x da tabela, embora esta estrutura deva ser utilizada em aplicações em que predominam inserções e pesquisas. Para eliminarmos um elemento x da tabela, começaremos por fazer uma pesquisa e caso esse valor seja encontrado, suponhamos que existia no índice j, então colocaríamos o campo ocupação da tabela nessa posição a falso e teríamos que verificar se o elemento seguinte (posição i) estava fora da posição dada pela função de hash (posição r), isto é se r não estivesse entre j e i, teria que ocupar o lugar de j e agora j tomaria o valor de i e tudo se repetiria, até encontrar-se uma posição vazia. Caso contrário, o elemento da posição i estava na posição correcta, então avançar-se-ia para o próximo i até encontrar uma posição livre ou um valor fora da posição devida. Um algoritmo possível será o seguinte: Algoritmo Inicio Pesquisa, considera-se que encontrou na posição j Se (enc==1) Então Repete tabela[j]. ocupaçao=falso i=(j+1)%d Repete Se (tabela[i]. ocupa çao==verdade) Então r=h (tabela[i]. conteudo) Se (((r>j) e (r< =i)) ou ((r>j) e Instituto Superior de Engenharia do Porto 6

Fim (i<j)) ou ((r<=j) e (i<j))) Então r_entreji=1 Senão r_entreji=0 Até (tabela [i].ocupaçao ==falso ou r_entreji=0) Se (r_entreji=0 e tabela[i].ocupaçao=verdade) Então tabela[j].conteudo=tabela[i]. conteudo tabela[j]. ocupaçao=verdade j=i Até (tabela[i]. ocupaçao ==falso) Senão Elemento não existe Para verificar se r se encontra entre a posição j e a posição i três situações serão possíveis: j r i i j r r i j Comparação de métodos de pesquisa Já vimos quatro métodos de pesquisa, pesquisa sequencial, pesquisa binária, pesquisa em árvore binária e hashing. A ordem de complexidade temporal Instituto Superior de Engenharia do Porto 7

destes métodos é normalmente determinada através do número médio de comparações necessárias para localizar um elemento na estrutura. Vimos que na pesquisa sequencial era de O(n) e na pesquisa binária e árvore binária de pesquisa era de O( log 2 n). A análise no caso de hashing é um pouco mais complicada, uma vez que depende da qualidade da função de hash e do tamanho da tabela. Uma boa função de hash, fornece uma distribuição uniforme dos valores de hash e quando combinado com uma tabela relativamente grande, o número de colisões é reduzido. Numa tabela de hash podemos definir o factor de ocupação (λ ) como sendo o número de elementos inseridos na tabela (m) sobre o tamanho da tabela (n), λ = m n. Quando a tabela está vazia λ = 0. À medida que se inserem elementos na tabela, λ vai aumentando, e maior é a probabilidade de colisão. No caso de endereçamento aberto, o seu valor máximo será 1 quando estiver completamente preenchida (m=n). No caso de encadeamento externo, como as listas de colisão podem crescer tanto quanto o que precisamos, λ pode ser maior do que 1. A análise formal da complexidade do hashing está fora do âmbito desta disciplina mas podemos apresentar os resultados obtidos por Knuth neste campo. Assim, chegou às fórmulas abaixo indicadas, para determinação do número médio de acessos necessários para uma pesquisa com sucesso e sem sucesso no caso de encadeamento externo e no caso de endereçamento aberto. Resolução de Colisões Pesquisa com Sucesso Pesquisa sem Sucesso Endereçamento [1 2(1-λ)] + λ2 [1 2(1-λ) 2 ] + λ2 Encadeamento 1 + λ2 e -λ + λ Esta tabela mostra que o endereçamento aberto é bom desde que o factor de ocupação se mantenha baixo (< 75%). De uma forma geral o encadeamento é um método melhor. Se λ=1 o encadeamento exige 1.5 acessos para uma pesquisa com sucesso, mas o método por endereçamento requer, com a tabela cheia M72 acessos. Sem dúvida, o hashing é um processo de pesquisa extremamente rápido, contudo cada um dos métodos tem a sua utilização. Instituto Superior de Engenharia do Porto 8

A pesquisa sequencial é eficiente quando o número de elementos é pequeno e os dados não tem que estar ordenados. A pesquisa binária é mais rápida mas exige que os dados estejam ordenados em array. Este tipo de pesquisa não é viável em situações em que os dados são adquiridos em tempo de execução( como tabelas de símbolos), uma vez que um array ordenado é um veículo ineficiente para inserções e eliminações. Por estas razões hashing e árvore binária de pesquisa competem,a árvore binária de pesquisa não é tão rápida mas tem o aspecto interessante de fornecer dados ordenados através de uma visita simétrica. Hasihng é o melhor método quando se pretende acesso rápido e não é requerido informação ordenada. Aplicações Aplicações desta estrutura são muitas, nomeadamente em compiladores, onde são usadas para manter o controlo das variáveis declaradas no código fonte, são conhecidas por tabelas de símbolos. São realmente as aplicações ideais para esta estrutura, uma vez que as operações realizadas são as inserções e pesquisas. Os identificadores de variáveis são geralmente pequenos e portanto são de computação rápida. Outra aplicação vulgar é nos programas de jogos. Á medida que o programa procura através de diferentes linhas de jogada, conserva o registo de cada posição ocorrida, calculando uma função de hash baseada nessa posição e "guardando" o movimento para essa posição. Se a mesma posição volta a ocorrer, geralmente por uma simples transposição de movimentos, o programa pode evitar cálculos dispendiosos. Esta acção genérica, feita em programas de jogos, é conhecida como "tabela de transposição". Ainda outra aplicação é na detecção de erros de ortografia. É feito hash a um diccionário completo e as palavras pode ser verificadas em tempo constante. As tabelas de hash são também indicadas para isto, uma vez que não é preciso corrigir as palavras, mas simplesmente indicar que não estão correctamente escritas. Instituto Superior de Engenharia do Porto 9