Чтение онлайн

на главную - закладки

Жанры

Фундаментальные алгоритмы и структуры данных в Delphi

Бакнелл Джулиан М.

Шрифт:

begin

inherited Create;

FDispose := aDispose;

if not Assigned(aHashFunc) then

htcError(tdeHashTblNoHashFunc, 'Create');

FHashFunc := aHashFunc;

FTable := TList.Create;

FTable.Count := TDGetClosestPrime(aTableSize);

FNodeMgr := TtdNodeManager.Create(sizeof(THashedItem));

htcAllocHeads(FTable);

FMaxLoadFactor := 5;

end;

destructor TtdHashTableChained.Destroy;

begin

if (FTable <> nil) then begin

Clear;

htcFreeHeads(FTable);

FTable.Destroy;

end;

FNodeMgr.Free;

inherited Destroy;

end;

Созданный

нами диспетчер узлов предназначен для работы с узлами THashItem. Он определяет структуру записей этого типа. Эта структура во многом аналогична структуре записей класса TtdHashLinear, за исключением того, что требуется связное поле и не требуется поле "используется" (все элементы в связном списке "используются" по определению;

удаленные из хеш-таблицы элементы в связном списке отсутствуют).

type

PHashedItem = ^THashedItem;

THashedItem = packed record

hiNext : PHashedItem;

hiItem : pointer;

{$IFDEF Delphi1}

hiKey : PString;

{$ELSE}

hiKey : string;

{$ENDIF}

end;

Конструктор вызывает метод htcAllocHeads для создания первоначально пустой хеш-таблицы. Вот что должно при этом происходить. Каждая ячейка в хеш-таблице будет содержать указатель на односвязный список (поскольку каждая ячейка содержит только указатель, для хранения хеш-таблицы можно воспользоваться TList). Для упрощения вставки и удаления элементов мы выделяем заглавные узлы для каждого возможного связного списка, как было описано в главе 3. Естественно, деструктор должен освобождать эти заглавные узлы - данная операция выполняется при помощи метода htcFreeHeads.

Листинг 7.13. Выделение и освобождение заглавных узлов связных списков

procedure TtdHashTableChained.htcAllocHeads(aTable : TList);

var

Inx : integer;

begin

for Inx := 0 to pred(aTable.Count) do

aTable.List^[Inx] := FNodeMgr.AllocNodeClear;

end;

procedure TtdHashTableChained.htcFreeHeads(aTable : TList);

var

Inx : integer;

begin

for Inx := 0 to pred(aTable.Count) do

FNodeMgr.FreeNode(aTable.List^[Inx]);

end;

Теперь посмотрим, как выполняется вставка нового элемента и его строкового ключа в хеш-таблицу, которая использует связывание.

Листинг 7.14. Вставка нового элемента в хеш-таблицу со связыванием

procedure TtdHashTableChained.Insert(const aKey : string; aItem : pointer );

var

Inx : integer;

Parent : pointer;

NewNode : PHashedItem;

begin

if htcFindPrim(aKey, Inx, Parent) then

htcError(tdeHashTblKeyExists, 'Insert');

NewNode := FNodeMgr.AllocNodeClear;

{$IFDEF Delphi1}

NewNode^.hiKey := NewStr(aKey);

{$ELSE}

NewNode^.hiKey := aKey;

{$ENDIF}

NewNode^.hi Item := aItem;

NewNode^.hiNext := PHashedItem(Parent)^.hiNext;

PHashedItem(Parent)^.hiNext := NewNode;

inc(FCount);

{увеличить

таблицу, если значение коэффициента загрузки превышает максимальное значение}

if (FCount > (FMaxLoadFactor * FTable.Count)) then

htcGrowTable;

end;

Прежде всего, мы вызываем подпрограмму htcFindPrim. Она выполняет такую же операцию, как и htllIndexOf, при использовании линейного зондирования: предпринимает попытку найти ключ и возвращает индекс ячейки, в которой он был найден. Однако этот метод создан с учетом применения связных списков. Если поиск ключа выполняется успешно, метод возвращает значение "истина", а также индекс ячейки хеш-таблицы и указатель на родительский узел элемента в связном списке. Почему родительский? Что ж, если вспомнить сказанное в главе 3, основные операции с односвязным списком предполагают вставку и удаление узла за данным узлом. Следовательно, целесообразнее, чтобы метод htcFindPrim возвращал родительский узел узла, в который выполняется вставка.

Если ключ не найден, метод htcFindPrlm возвращает значение "ложь" и индекс ячейки, в которую нужно вставить элемент, и родительский узел, за которым его можно успешно вставить.

Итак, вернемся к методу Insert. ЕстестЁенно, если ключ был найден, метод генерирует ошибку. В противном случае мы выделяем новый узел, устанавливаем элемент и ключ, а затем вставляем элемент непосредственно за данным узлом.

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

Как легко догадаться, метод Delete работает аналогично.

Листинг 7.15. Удаление элемента из хеш-таблицы со связыванием

procedure TtdHashTableChained.Delete(const aKey : string);

var

Inx : integer;

Parent : pointer;

Temp : PHashedItem;

begin

{поиск ключа}

if not htcFindPrim(aKey, Inx, Parent) then

htcError(tdeHashTblKeyNotFound, 'Delete');

{удалить элемент и ключ из данного узла}

Temp := PHashedItem(Parent)^.hiNext;

if Assigned(FDispose) then

FDispose(Temp^.hiItem);

{$IFDEF Delphi1}

DisposeStr(Temp^.hiKey);

{$ELSE}

Temp^.hiKey := '';

{$ENDIF}

{разорвать связь с узлом и освободить его}

PHashedItem(Parent)^.hiNext := Temp^.hiNext;

FNodeMgr.FreeNode(Temp);

dec(FCount);

end;

Мы предпринимаем попытку найти ключ (если он не найден, метод генерирует ошибку), а затем избавляемся от содержимого возвращенного элемента и удаляем его из связного списка. Обратите внимание на простоту кода обеих методов, Insert и Delete, обусловленную наличием заглавного узла в каждом связном списке. Не нужно беспокоиться о том, является ли родительский узел нулевым. Метод htcFindPrlm всегда будет возвращать допустимый родительский узел.

Поделиться:
Популярные книги

Менталист. Эмансипация

Еслер Андрей
1. Выиграть у времени
Фантастика:
альтернативная история
7.52
рейтинг книги
Менталист. Эмансипация

Кодекс Охотника. Книга V

Винокуров Юрий
5. Кодекс Охотника
Фантастика:
фэнтези
попаданцы
аниме
4.50
рейтинг книги
Кодекс Охотника. Книга V

Виконт. Книга 1. Второе рождение

Юллем Евгений
1. Псевдоним `Испанец`
Фантастика:
фэнтези
боевая фантастика
попаданцы
6.67
рейтинг книги
Виконт. Книга 1. Второе рождение

Эволюция мага

Лисина Александра
2. Гибрид
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Эволюция мага

(Не)свободные, или Фиктивная жена драконьего военачальника

Найт Алекс
Любовные романы:
любовно-фантастические романы
5.00
рейтинг книги
(Не)свободные, или Фиктивная жена драконьего военачальника

Месть Паладина

Юллем Евгений
5. Псевдоним `Испанец`
Фантастика:
фэнтези
попаданцы
аниме
7.00
рейтинг книги
Месть Паладина

Новый Рал

Северный Лис
1. Рал!
Фантастика:
фэнтези
попаданцы
5.70
рейтинг книги
Новый Рал

Академия

Кондакова Анна
2. Клан Волка
Фантастика:
боевая фантастика
5.40
рейтинг книги
Академия

Прометей: владыка моря

Рави Ивар
5. Прометей
Фантастика:
фэнтези
5.97
рейтинг книги
Прометей: владыка моря

Тринадцатый

NikL
1. Видящий смерть
Фантастика:
фэнтези
попаданцы
аниме
6.80
рейтинг книги
Тринадцатый

Мастер темных Арканов

Карелин Сергей Витальевич
1. Мастер темных арканов
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Мастер темных Арканов

Дайте поспать! Том II

Матисов Павел
2. Вечный Сон
Фантастика:
фэнтези
постапокалипсис
рпг
5.00
рейтинг книги
Дайте поспать! Том II

Возрождение Феникса. Том 1

Володин Григорий Григорьевич
1. Возрождение Феникса
Фантастика:
фэнтези
попаданцы
альтернативная история
6.79
рейтинг книги
Возрождение Феникса. Том 1

Его темная целительница

Крааш Кира
2. Любовь среди туманов
Фантастика:
фэнтези
5.75
рейтинг книги
Его темная целительница