Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
Хеш.doc
Скачиваний:
3
Добавлен:
23.09.2019
Размер:
98.82 Кб
Скачать

19. Методы разрешения коллизий: внешние и внутренние цепочки, линейное и квадратичное опробование, смена системы счисления, применение суперпозиции двух «близких» хеш-функций.)

Разрешение коллизий при хешировании методом открытой адресации.

Посмотрим, что произойдет, если мы захотим ввести в таблицу некоторый новый номер изделия 0596397. Используя хеш-функцию h(key):=key mod 1000, мы найдем, что h (0596397) =397 и что запись для этого изделия должна находиться в позиции 397 в массиве. Однако позиция 397 уже занята, поскольку там находится запись с ключом 4957397. Следовательно, запись с ключом 0596397 должна быть вставлена в таблицу в другом месте.

Самым простым методом разрешения коллизий при хешировании является помещение данной записи в следующую свободную позицию в массиве. Например, запись с ключом 0596397 помещается в ячейку 398, которая пока свободна, поскольку 397 уже занята. Когда эта запись будет вставлена, другая запись, которая хешируется в позицию 397 (с таким ключом, как 8764397) или в позицию 398 (с таким ключом, как 2194398), вставляется в следующую свободную позицию, которая в данном случае равна 400.

Если ячейка массива h(key) уже занята некоторой записью с другим ключом, то функция rh применяется к значению h(key) для того, чтобы найти другую ячейку, куда может быть помещена эта запись. Если ячейка rh(h(key)) также занята, то хеширование выполняется еще раз и проверяется ячейка rh(rh(h(key))). Этот процесс продолжается до тех пор, пока не будет найдена пустая ячейка. Rh - это функция повторного хеширования, которая воспринимает один индекс в массиве и выдает другой индекс.

Var     K: array [0...999] of integer; Function h(key: integer): integer; Begin     h := key mod 1000; End; Function rh(i: integer): integer; Begin     rh:=i+1 mod 1000; End; Procedure insert(key: integer); Var     I: integer; begin I := h(key); {хешируем ключ} while ((k(i)< >key) and (k(i)< >0)) do     i := rh(i); {мы должны выполнить повторное хеширование} if k(i) =0 then {вставляем запись в пустую позицию} k(i)=key end;

Недостатки метода.

Во-первых, он предполагает фиксированный размер таблицы. Если число записей превысит этот размер, то их невозможно вставлять без выделения таблицы большего размера и повторного вычисления значений хеширования для ключей всех записей, находящихся уже в таблице, используя новую хеш-функцию.

Во -вторых, из такой таблицы трудно удалить запись.

Разрешение коллизий при хешировании методом цепочек

Он представляет собой организацию связанного списка из всех записей, чьи ключи хешируются в одно и то же значение.

75 66 42 192 91 40 49 87 67 16 417 130 372 227

Рис. Разрешение коллизий при хешировании методом цепочек.

type link = ^node; node = record key: integer; st: string; next: link; end; var mas: array[0..9] of link; function h(key: integer): integer; begin h:=key mod 10; end; function search(key1: integer; st1: string): link; var i: integer; q, p, s: link; begin i:= h(key1); q:=nil; p:=mas[i]; while p <> nil do begin if p^.key = key1 then begin     search:=p;     exit;        end; q := p; p := p^.link; end; {Если ключ не найден, вставляем новую запись} new(s); s^.key:=key1; s^.st:=st1; s^.next:=nil; if q = nil then mas[i]:=s else q^.next:=s; search:=s; end;

Удаление узла из таблицы, которая построена по методу цепочек, заключается просто в исключении узла из связанного списка. Удаленный узел никак не влияет на эффективность алгоритма поиска. Алгоритм будет работать так, как если бы этот узел никогда не вставлялся в таблицу. Отметим, что эти списки могут быть динамически переупорядочены для получения большей эффективности поиска.

Основным недостатком метода цепочек является то, что для узлов указателей требуется дополнительное пространство.

Линейное опробование (рис. 29, а) сводится к последовательному

перебору сегментов таблицы с некоторым фиксированным шагом:

адрес = h(x) + c·i ,

где i – номер попытки разрешить коллизию; c – константа, определяю-

щая шаг перебора. При шаге, равном единице, происходит последова-

тельный перебор всех сегментов после текущего.

Квадратичное опробование (рис. 29, б) отличается от линейного тем,

что шаг перебора сегментов нелинейно зависит от номера попытки найти

свободный сегмент:

адрес = h(x) + c·i + d·i2,

где i – номер попытки разрешить коллизию, c и d – константы.

Благодаря нелинейности такой адресации уменьшается число проб

при большом числе ключей-синонимов.

Однако даже относительно небольшое число проб может быстро при-

вести к выходу за адресное пространство небольшой таблицы вслед-

ствие квадратичной зависимости адреса от номера попытки.

Еще одна разновидность метода открытой адресации, которая назы-

вается двойным хешированием (рис. 29, в), основана на нелинейной ад-

ресации, достигаемой за счет суммирования значений основной и

дополнительной хеш-функций

100

адрес = h1(x) + i·h2(x).