Algoritmos e Estruturas de Dados II

Luiz Gustavo Bizarro Mirisola Gustavo Sousa Pavani

Universidade Federal do ABC (UFABC) 1º Trimestre - 2011
Aulas 8 e 9

1

Busca em tabelas
Para se resolver os problemas de busca, inserção e remoção em uma tabela com N elementos, há várias maneiras: Busca seqüencial.

Acesso em O(N)

Busca Binária

Acesso em O(log2N) Ainda muito lento se N é grande ((log21M)=20!

Uso de árvores B.

Acesso em O(logkN), onde k é o tamanho da folha. Acesso bem melhor do que na busca seqüencial!

2

Busca em tabelas
Não é possível achar um método mais rápido? O(n) não é ruim em memória, mas mesmo um método logaritmico em seeks é custoso. Lembra: acesso disco 1.000.000 vezes mais lento Tabelas de dispersão (Hash Tables). Busca com acesso médio igual a O(1). O(1) significa “constante”

Não é necessariamente 1 único acesso Alem disso, é O(1) na média! O pior caso é O(N) Na medida do possível, uma constante pequena! Idéia: tempo de acesso não deve variar com N!!

3

Armazenamento em tabelas
Suponha que existam n chaves a serem armazenadas em uma tabela T, seqüencial e de dimensão m. As posições da tabela se situam no intervalo [0,m-1]. Tabela é particionada em m compartimentos “buckets”, cada um correspondendo a um endereço e podendo armazenar r registros distintos.

Se a tabela está na memória: comum r=1. Se a tabela está armazenada no disco:
– –

número de acessos é mais importante do que o uso eficiente da memória. cada compartimento ocupa, tipicamente, um bloco de disco.

Vamos considerar somente o primeiro caso (r=1).

Buckets

4

. Considere que os valores das chaves sejam. Essa técnica também pode ser utilizada quando n < m. porém (m-n) é pequeno.(m-1).1.Acesso direto Suponha que o número de chaves n seja igual ao número de compartimentos m... ● As chaves se distribuiriam entre 0..1. 0....(m-1). respectivamente. Haveriam (m-n) compartimentos vazios ou espaços.. Técnica denominada de acesso direto. ● 5 . Pode-se utilizar então diretamente o valor de cada chave como seu índice na tabela! ● Cada chave x é armazenada no compartimento x.

Problemas com o acesso direto As chaves nem sempre são valores numéricos. f (chave)  [0. Exemplo: chaves como nome de pessoas. Acesso direto: tabela de 1 M buckets. Precisamos de um bucket para cada chave possível? 6 . Solução: o uso de tabelas de dispersão.n − 1] → Função ● O problema dos espaços é mais grave! Exemplo: duas chaves 0 e 999999... Solução: construa representação numérica das chaves. sendo que somente dois seriam ocupados.

Tabela de dispersão Em lugar de organizar a tabela segundo o valor relativo de cada chave em relação às demais. não n ! 7 .. a tabela de dispersão leva em conta somente o seu valor absoluto. interpretado como um valor numérico. a chave é transformada em um endereço da tabela (endereço base) hash(chave)  [0.m − 1] → Note: m.. Através da aplicação de uma função conveniente (função de dispersão ou função hash).

Tabela de dispersão Em lugar de organizar a tabela segundo o valor relativo de cada chave em relação às demais. Através da aplicação de uma função conveniente (função de dispersão ou função hash). a chave é transformada em um endereço da tabela (endereço base). a tabela de dispersão leva em conta somente o seu valor absoluto. interpretado como um valor numérico. 8 . entretanto O(N). sendo o pior caso. O método aproveita a possibilidade de acesso randômico a memória para alcançar uma complexidade média O(1).

Hashing: duas chaves diferentes podem ser transformadas para o mesmo endereço base. Quando isso ocorre. que deve ser apropriadamente tratada. 9 . mesmo que a chave seja usada para determinar a localização do registro. Não há relação óbvia entre a chave e a localização do registro correspondente. de forma que dois registros podem ser enviados para um mesmo local no arquivo.Diferenças das tabelas de dispersão Hashing: o endereço base gerado pela função de dispersão parece ser aleatório. dizemos que acontece uma colisão.

Inicializada vazia 10 . m = 11.Exemplo Tabela.

h(Steve) = 3 l l l l l Para buscar “Steve” na tabela: Calcule h(Steve).Exemplo Adicionado a chave “Steve”. 11 . RNN ou byte offset armazenado junto com a chave. obtenha 3 Vá diretamente à posição 3 e encontre “Steve” Hash table pode ser índice.

Exemplo Adicionado “Sparck”: OK. não há nada: l Conclusão: l Zé não está na tabela 12 . h(Spark)=6 l l Calcule h(Zé)=9 Leia a posição 9.

Exemplo Adicionado “Notes”: Problema: h(Notes)=3 l h(Notes)=h(Steve) l Colisão!! l Há mais que 11 strings no mundo l Com função hash uniforme. qual a chance de colisão? l Depende: l tamanho da tabela l quantas chaves devem ser armazenadas. 13 .

Exemplo Adicionado “Notes”: Problema: h(Notes)=3 l h(Notes)=h(Steve) l Colisão!! l Várias formas de tratar colisões l Onde armazenar e como encontrar Notes e Steve agora. l Importante: l Colisões diminuem a eficiência. 14 .

● Se a função hash for O(n). deve satisfazer as seguintes condições: Produzir um número baixo de colisões. há sempre colisões com n chaves armazenadas.Função de dispersão Uma função de dispersão h transforma uma chave x em um endereço base h(x) da tabela de dispersão. Se m < n. ● Igual probabilidade de escolha para todos os buckets 15 . qual a vantagem? Ser uniforme. ● Ser facilmente computável. Idealmente. ● Algum conhecimento é necessário sobre as chaves.

somente uma em 10120000 é capaz de evitar todas as colisões. Entretanto. Foi mostrado que de todas as funções de dispersão possíveis. é muito difícil se obter uma função de dispersão perfeita. Suponha que eu gostaria de guardar 4000 registros em 5000 compartimentos. Para um número pequeno de chaves que mudam com pouca freqüência é possível achar com mais facilidade uma função de dispersão perfeita. Supondo-se determinados n e m. 16 .Função de dispersão perfeita Quando uma função de dispersão é capaz de evitar qualquer colisão tal função é dita perfeita. Foco: minimizar o número de colisões a um valor aceitável.

largamente empregado. eficiente. Potências de 2 são ainda piores! – ● Ignora os bits mais significativos da chave. 17 ● .Hash Function: Método da divisão Fácil. H(x) = x mod m. ● Sugestão: escolher m tal que não possua divisores primos menores que 20. Cuidado na escolha do valor de m! ● Números pares são ruins. A chave x é dividida pela dimensão da tabela m e o resto da divisão é o endereço base. Ou um número primo de tamanho apropriado. m-1]. Resulta em endereços no intervalo [0.

Hash Function: Método da dobra Suponha a chave como uma seqüência de dígitos escritos em um pedaço de papel. dk... O processo é repetido. d’j.1  i  j. Outra maneira de se ober um endereço base de k bits para uma chave qualquer é separar a chave em diversos campos de k bits e operá-los com o ou exclusivo.. d2j+1. Estes devem ser somados. 18 . onde d’i é o dígito menos significativo da soma di+d2j-i+1. O número total de dobras e posição j de cada uma devem ser definidos de forma que o resultado final contenha o número de dígitos desejado para formar o endereço base... sem levar em consideração o “vai um”.. Há várias variações dependendo da chave. O método consiste em “dobrar” esse papel de maneira que os dígitos se superponham. . Suponha que os dígitos decimais da chave sejam d1.. Isto implica transformar a chave em d’1..dk e que uma dobra seja realizada após o j-ésimo dígito da esquerda....

Hash Function: Multiplicação Apresenta algumas variações. descartando-se os bits excessivos da extrema direita e da extrema esquerda da palavra. A chave é multiplicada por ela mesma (ou alternativamente por uma constante) e o resultado é armazenado em uma palavra de memória de b bits. O número de bits necessário para formar o endereço base é então retirado dos b bits. 19 . O número b bits determina o número possível de endereços base. sendo a mais conhecida o método do “meio quadrado”.

caso a caso. 20 .Hash Function: Aleatória x Uniforme Idéia: um liquidificador de bits. com distribuição da saída mais uniforme possível Outro exemplo: criptografar a chave Usar chave cifrada como índice no hash Pergunta: Uniforme = Aleatória ? Distribuções melhores que aleatória: Uniformidade com função específica para as chaves.

aqueles que possuem o menor desvio. de forma conveniente. na verdade. isto é.Método da análise dos dígitos Ao contrário dos demais. alguns dígitos decimais que formam a chave para compor o seu endereço base. ni chaves com primeiro dígito i. Se existem n chaves diferentes. 0  i  9. em média. É usado em geral para chaves decimais. n/10 devem ter o primeiro dígito zero. 21 . Deve-se calcular o desvio da distribuição do primeiro dígito. A função de dispersão consiste em selecionar. necessita de conhecimento prévio do tipo de chave de busca. Inicialmente. n/10 devem ter o primeiro dígito 1 e assim sucessivamente. então. (ni − n / 10) 2 ∑ i =0 9 Repita a análise para cada dígito e encontre os k melhores. O endereço base é obtido eliminando-se todos os dígitos da chave. Suponha que existem. observa-se o primeiro dígito de cada chave. Suponha que os valores das chaves sejam uniformemente distribuídos. exceto os escolhidos entre os k melhores.

o valor é desprezível.. E assim por diante. aleatória ● ● ● ( r / m) x e − r / m p( x) = x! Quantos compartimentos ficaram vazios = N p(0) Quantos compartimentos terão um registro = N p(1) Quantos compartimentos terão mais de um registro alocado = N [p(2) + p(3) + p(4) + p(5)]. Para p(3). um no compartimento e outro fora. quantos registros fora do endereço base são esperados = N [p(2) + 2p(3) + 3p(4) + 4p(5)] ● Para p(2). Para x  6. Supondo um registro por compartimento. 22 .. um no compartimento e dois fora.Memória extra para reduzir colisões Fator de carga (# registros armazenados)/(# de compartimentos) = r / m. Medida da quantidade de memória ou espaço em disco que é realmente usado. Fórmula de Poisson Vale para distr.

8 9.8 28.6 17.4 24.8 23 .4 13.1 31.2 34.1 36.6 21. provocando outra colisão no futuro! (Depende de como colisões são tratadas) Efeito acumulativo Colisões secundárias Fator de carga (%) 10 20 30 40 50 60 70 80 90 100 % de registros não armazenados 4. Note: esse resultado não conta toda a estória. supondo um registro por compartimento. pois um registro fora do seu endereço base pode ocupar o endereço base de outro registro.Efeito do fator de carga Efeito do fator de carga na proporção de registros não armazenados no endereço base.

Armazenar chaves sinônimas em listas encadeadas Ou fora da tabela (sem provocar novas colisões) Ou compartilhando o espaço da tabela. sempre existe a possibilidade da ocorrência de colisões. Necessidade: método de tratamento de colisões. A diminuição de fator de carga não elimina todas as colisões.Tratamento de colisões Mesmo que uma função de dispersão seja muito boa. 24 . apenas as torna mais raras Outro problema: desperdício de espaço para baixos fatores de carga.

Encadeamento exterior Consiste em manter m listas encadeadas. Os registros correspondentes aos endereços base serão os nós cabeça para essas listas. Campo de encadeamento deve ser acrescentado a cada registro. Implementação: simples aplicação de lista encadeada. uma para cada possível endereço base. 25 .

temos para uma função de dispersão uniforme: ● Busca sem sucesso: número médio de comparações efetuadas é igual ao fator de carga (α). Busca com sucesso: 1 + α/2 – 1/2m. entretanto.Encadeamento exterior O comprimento de uma lista encadeada pode ser O(N). logo esta é a complexidade de pior caso. ● Desvantagem: Espaço para ponteiros 26 . No caso médio.

27 . como uma lista encadeada. não é desejável a manutenção de uma estrutura exterior à tabela de dispersão. Neste caso.Encadeamento interior Em algumas aplicações. a lista encadeada deve compartilhar o mesmo espaço de memória da tabela de dispersão.

p + s = m. Os valores de p e s são fixos. ● Dois campos no compartimento. a função de dispersão deve obter endereços base na faixa [0. de tamanho s. de tamanho p. 28 .p-1]. uma de endereços base. Assim. Naturalmente. e outra reservada aos sinônimos. Um para o armazenamento da chave e outro um ponteiro que indica o próximo elemento da lista de sinônimos correspondentes ao endereço base em questão.Encadeamento interior O encadeamento interior prevê a divisão da tabela T em duas zonas.

29 . a tabela de dispersão de um bucket se reduz a uma lista encadeada. Entretanto.Encadeamento interior . ● Diminuição da eficiência. o que pode causar “overflow” caso ela já tenha sido preenchida. aumentando-se o tamanho da zona de colisão pela correspondente diminuição da zona de endereços base. mas essa técnica produz o efeito indesejado de colisões secundárias.problemas A zona de colisão tem tamanho limitado. aquelas provenientes da coincidência de endereços para chaves que não são sinônimas. isto é. Uma saída é não se diferenciar as duas zonas da tabela. causa também uma diminuição da eficiência da tabela de dispersão. No caso limite em que p = 1 e s = m – 1. cujo tempo médio de busca é O(N). ao diminuir a probabilidade de overflow.

ocupado e liberado.estado = “ocupado” então a  1. end  h(x). a) ● a  0. Uso de três estados: vazio. % chave encontrada senão end  T[end].estado = “não ocupado” então j  end. end. end  j. % chave não encontrada 30 ● ● ● ● ● ● ● . enquanto a = 0 faça se T[end]. j  nil. Está fora de cogitação re-organizar a tabela T. Procedimento busca(x.chave = x e T[end]. Supondo que cada registro contém três campos: chave. estado e ponteiro. se T[end].ponteiro se end = h(x) então a  2.Busca por encadeamento interior Tratamento da remoção de uma chave.

T[j]. end.ponteiro  temp. T[h(x)].Inserção por encadeamento interior busca(x. enquanto i  m faça se T[j] estado = “ocupado” então j  (j+1) mod m.ponteiro. chave já existente”.chave  x.ponteiro  T[h(x)]. j  h(x). % inserção de x T[j]. overflow”. 31 .estado  “ocupado” Senão “inserção inválida. senão i  1. a) se a  1 então se end  nil então j  end. pare temp  T[j]. senão i  m + 2 %compartimento não ocupado se i = (m + 1) então “inserção inválida. i  i + 1. % fusão das listas T[j].ponteiro.

32 .estado  “liberado” senão “exclusão inválida: chave não existente.Remoção por encadeamento interior busca(x.” A busca. end. a) se a = 1 então T[end]. inserção e remoção possuem pior caso igual a O(N).

e assim por diante. As chaves sinônimas são também armazenadas na tabela. A busca sem sucesso ocorre quando o cálculo indica um compartimento vazio ou a exaustão da tabela. Desperdício de espaço com ponteiros. sem qualquer informação adicional. Se ocorrer nova colisão. Solução: endereçamento aberto.Endereçamento aberto O tratamento de colisões por encadeamento utiliza listas encadeadas para armazenar chaves sinônimas. determina-se por cálculo qual o próximo compartimento a ser examinado. A busca com sucesso se encerra quando o compartimento for encontrado contendo a chave procurada. Quando houver colisão. um novo compartimento é escolhido por cálculo. 33 .

(m-1). k).k)]. para k = 0. onde k = 0. a) a = 3.Endereçamento aberto . k = m. k). Mecanismo de busca procedimento busca-aberto(x...(m-1).k)]. k).. A seqüência de endereços base h(x.busca A função de dispersão h(x) deve ser capaz de fornecer até m endereços base. se T[h(x.chave = nil então a = 2. end. % posição livre senão k = k + 1 34 . enquanto k < m faça end = h(x. % chave encontrada senão se T[h(x..... é chamada de seqüência de tentativas. Nova forma: h(x. k = 0.chave = x então a = 1.. k = m.

e a busca deve continuar 1 1 1 log + α 1−α α 35 . ocupado e liberado para permitir a remoção de elementos.α) tentativas e busca com sucesso faz Como no caso do encadeamento interior. busca sem sucesso faz 1/(1.Endereçamento aberto .remoção Busca e inserção tem pior caso igual a O(N) Na média. para α < 1. cada compartimento da tabela deve ter três estados: vazio. Liberado: marcador (tombstone) indica que o registro já foi ocupado.

maior a probabilidade de aumentá-lo ainda mais! 36 . tenta-se h’(x) + 2. de chave x. h(x.. o que se denomina agrupamento primário. etc. k) = (h’(x) + k) mod m.. Se este estiver ocupado. . no endereço consecutivo h’(x) + 1.. até uma posição vazia.Tentativa linear Suponha que o endereço base da chave x é h’(x) e que exista uma chave x’ que ocupa o compartimento h’(x). 0 <= k <= (m – 1) Possui o inconveniente de produzir longos trechos consecutivos de memória ocupados. ● Quanto maior for o tamanho do agrupamento primário. Solução: armazenar o novo registro. considerando-se a tabela circular.

Morris foi apagado 3.agora se busca por Smith. mas há um espaço livre entre o slot 5 e a posição de Smith. A busca não pode parar no slot 7 só poque está livre 37 .Tentativa linear – porque a tombstone 1.registros abaixo foram inseridos na ordem dada 2.

h(x. A degradação é menor que no agrupamento primário. Forma recorrente ● h(x. Esse fato produz uma outra forma de concentração de chaves na tabela. c2  0 e 0  k  (m – 1). c1 e c2 são constantes. se duas chaves possuírem a mesma tentativa inicial. ● ● 38 . c1 e c2 devem ser escolhidos de forma que consigam varrer toda a tabela. denominada de agrupamento secundário. é possível varrer toda a tabela. k) = (h’(x) + c1 k +c2 k2) mod m.0) = h’(x) h(x.Tentativa quadrática Tentativa de se obter seqüências de endereços diversos para endereços base próximos. m.k) = (h(x. Entretanto.k-1)+k) mod m Se m for potência de 2. O incremento é dado por uma função quadrática de k. as duas seqüências de tentativas serão idênticas. porém diferentes.

mais fácil ainda.Dispersão dupla A seqüência de alternativas é calculada como: h(x. então as seqüências obtidas por esses métodos serão idênticas. Para que os endereços bases obtidos correspondam a varredura de toda a tabela. k) = (h’(x)+ k h’’(x)) mod m. Também é capaz de gerar um maior número de tentativas distintas. Na dispersão dupla. 39 . ● Se x e y são duas chaves distintas tais que h’(x) = h’(y). 0 <= k < m. Esse método tende a distribuir as chaves na tabela de forma mais conveniente que a tentativa linear ou a quadrática. – ● ● ● Exemplo: m como potência de 2 e h’’(x) gerando números ímpares ou. é necessário que h’’(x) e m sejam primos entre si. m primo. o que ocasiona a concentração de chaves em trechos da tabela. isso somente acontece se h’(x) = h’(y) e h’’(x) = h’’(y) são idênticas.

poderá ser recomeçado. Quando todos os compartimentos tiverem sido expandidos. é distribuído entre os compartimentos p e q. denominado de expansão de p. Pode não ser possível avaliar de antemão o número de elementos da tabela Solução simples: método da dispersão linear. pela facilidade de ajustes nos ponteiros. O conjunto de chaves sinônimas. ● Alterar apenas parte dos endereços já alocados. Expansão de um compartimento p com a definição de um novo compartimento q no final de tabela. quando necessário. originalmente com endereço base p. Quando se alcançar determinado limiar no fator de carga. Normalmente o tratamento de colisões é por encadeamento exterior.Tabela de dispersão dinâmica Até o momento. o número de compartimentos da tabela não variava. a tabela pode ser expandida. 40 . o tamanho da tabela terá sido dobrado e o processo.

Seja p o menor compartimento ainda não expandido. todos os endereços serão calculados por h1(x). o compartimento já foi expandido. computa-se h0(x). É necessário conhecer l. A função de dispersão é então fornecida por: ● hl(x) = x mod (2l m) 41 . o endereço correto é então recalculado com a função h1(x) Note que quando toda a tabela tiver sido expandida. que é o número de vezes que a tabela foi duplicada. Se h0(x) < p.Processo de expansão Dada uma chave x.

Sign up to vote on this title
UsefulNot useful