8.2. Схема хеширования

Выберем М - предельный размер массива записей H несколько больший чем n (n<M). Позиции в массиве будем нумеровать начиная с нуля. Задача состоит в том, чтобы подобрать функцию i=h(key), которая по возможности равномерно отображает значения ключа key на интервал . Чаще всего используютi=key mod M. В этом случае если K=M реализуется идеальная расстановка элементов в хеш-таблице.

Функция h(key) называется функцией расстановки или хеш-функцией. Ввиду того, что число возможных значений ключа K обычно значительно превосходит возможное количество записей K>>n, любая функция расстановки может для нескольких значений ключа давать одинаковое значение позиции i в таблице. Например, если M=10 то для key=1, key=11 и key=101 позиция i=h(key)=1. В этом случае h(key) только определяет позицию, начиная с которой нужно искать запись с ключом key. Поэтому схема хеширования должна включать алгоритм разрешения конфликтов, определяющий порядок действий, если позиция i=h(key) оказывается занятой записью с другим ключом.

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

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

Алгоритм поиска сначала находит по ключу позицию i в таблице, и если ключ не совпадает то последующий поиск осуществляется в соответствии с алгоритмом разрешения конфликтов начиная от позиции i.

Часто ключами таблиц являются не числа, а последовательность букв (строки). При выборе функции i=h(key) важно, чтобы ее значения вычислялись как можно проще и быстрее. Поэтому символьные ключи обычно заменяют целыми числами из некоторого диапазона. Например, если key - слово, то суммой кодов первых 2х-3х букв, если фраза (Ф.И.О.), то сумма кодов первых букв. При написании алгоритма разрешения конфликтов поиск ведут уже по полному слову.

8.3. Хеш-таблица на основе массива связанных списков

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

Обычно выбирают М - простое число, немного большее чем предполагаемое количество записей.

Хеш-функция выбирается в виде i:=Key mod M.

Работу с таблицей можно организовать посредством класса ТН, представленного на листинге 8.1:

Листинг 8.1

unit Unit2;

Interface

uses . . . . ;

Type

Tkey=Word;

Tinf=record

Fio:string;

key:Word;

end;

Psel=^sel;

sel=record

inf:Tinf;

A:Psel;

end;

Ms=array[0..1] of Psel;

Pms=^Ms;

TH=class(Tobject)

M,n:Word;

sp,sp1:psel;

H:Pms;

Constructor create(M0:word);

Destructor Free(var stringgrid:Tstringgrid);

Procedure Add(Inf:Tinf);

Procedure Read(key:Tkey; var Inf:Tinf);

Procedure Red(key:Tkey; var Inf:Tinf);

end;

implementation

Constructor TH.create(M0:word);

var i:word;

begin Inherited create;

M:=M0; n:=0;

Getmem(H,M*4);

for i:=0 to M-1 do begin H[i]:=Nil;

end;

Destructor TH.Free;// прочесть и удалить все записи

var i,j:word;

begin j:=0;

for i:=0 to M-1 do begin

While H[i]<>Nil do

begin Inc(j);

sp:=H[i];

StringGrid.Cells[0,j]:=sp^.inf.Fio;

StringGrid.Cells[1,j]:=IntToStr(sp^.inf.key);

H[i]:= H[i]^.A;

dispose(sp);

end;

dispose(H[i]); end;

FreeMem(H,4*M);

end;

Procedure TH.Add;// добавить новую запись

begin

i:=inf.key Mod M;

New(p); Inc(n);

p^.Inf:=Inf;

p^.A:=H[i];

H[i]:=p;

end;

Procedure TH.Read;//чтение без удаления

begin

i:=key mod M;

sp:=H[i];

While (sp<>Nil) and (sp^.inf.key<>key) do

sp:=sp^.A;

if sp<>Nil then inf:=sp^.inf

else ShowMessage('ключ не найден');

end;

Procedure TH.Red;//чтение с удалением

begin

i:=key mod M;

sp:=H[i]; sp1:=sp;

While (sp<>Nil) and (sp^.inf.key<>key) do

begin sp1:=sp; sp:=sp^.A end;

if sp<>Nil then begin

inf:=sp^.inf;

if sp1=sp then H[i]:=sp^.A

else sp1^.A:=sp^.A;

dispose(sp); Dec(n);

end

else ShowMessage('ключ не найден');

end;

end.

При освобождении таблицы (TH.Free) все записи считываются в StringGrid и освобождается память, выделенная под них, после этого освобождается память, выделенная под массив указателей. Вместо записи в StringGrid легко организовать запись в файл или Memo.

После описания этого класса работа с хэш-таблицей осуществляется, например, следующим образом:

uses unit2;

{$R *.dfm}

var H1:TH;

M0,n,i:word;