- •Введение
- •1. Технология проектирования и реализации коллекций данных.
- •1.1. Постановка задачи.
- •1.2. Разработка структур данных и алгоритмов
- •1.3. Кодирование
- •1.4. Отладка и тестирование
- •1.5. Сопровождение
- •2. Лабораторная работа «Сортировка коллекции»
- •2.1. Алгоритмы внутренней сортировки
- •2.2. Задание к лабораторной работе
- •2.1.1. Варианты заданий
- •2.1.2. Методические указания к выполнению задания
- •2.3. Контрольные вопросы и упражнения.
- •3. Лабораторная работа «Коллекция данных - список».
- •3.1. Структуры списков
- •3.2. Задание к лабораторной работе
- •3.2.1. Варианты заданий:
- •3.2.2. Методические указания к выполнению задания
- •3.3. Контрольные вопросы и упражнения.
- •4. Лабораторная работа «Коллекция данных - дерево поиска».
- •4.1. Структуры bst - деревьев
- •4.2. Задание к лабораторной работе
- •4.2.1. Варианты задания
- •4.2.2. Методические указания к выполнению задания:
- •4.3. Контрольные вопросы и упражнения.
- •5. Лабораторная работа «Коллекция данных - сбалансированное дерево поиска»
- •5.1. Структуры сбалансированных деревьев
- •5.2. Задание к лабораторной работе
- •5.2.1. Варианты заданий:
- •5.2.2. Методические указания к выполнению задания
- •5.3. Контрольные вопросы и упражнения.
- •6. Лабораторная работа «Коллекция данных - хеш – таблица»
- •6.1. Функции хеширования
- •6.2. Разрешение коллизий и структуры хеш-таблиц
- •6.3. Трудоёмкость операций
- •6.4. Задание к лабораторной работе
- •6.4.1. Варианты заданий:
- •6.4.2. Методические указания к выполнению задания
- •6.5. Контрольные вопросы и упражнения.
- •Литература
- •Приложение a: Псевдокод. Основные правила и соглашения псевдокода
- •Алгоритм сортировки Шелла
- •Алгоритм пирамидальной сортировки
- •Рекурсивный алгоритм сортировки разделением
- •Итеративный алгоритм сортировки разделением
- •Рекурсивный алгоритм сортировки слиянием
- •Итеративный алгоритм сортировки слиянием
- •Рекурсивный алгоритм поразрядной msd-сортировки
- •Итеративный алгоритм поразрядной lsd-сортировки
- •Итеративный алгоритм вставки элемента в bst – дерево
- •Рекурсивный алгоритм удаления элемента из bst – дерева
- •Алгоритм вставки элемента в корень bst – дерева
- •Алгоритм поиска предыдущего по значению ключа узла bst – дерева
- •Алгоритм поиска k –го по значению ключа узла в bst – дереве
- •Алгоритм разбиения дерева на части
- •Алгоритм удаления из bst-дерева на основе объединения поддеревьев
- •Алгоритм объединения bst – деревьев
- •Алгоритм удаления элемента из рандомизированного дерева
- •Алгоритм вставки элемента в avl-дерево
- •Рекурсивный алгоритм вставки элемента в rb – дерево
- •Итеративный алгоритм вставки элемента в rb – дерево
- •Итеративный алгоритм удаления элемента из rb – дерева
- •Алгоритм вставки элемента в 2-3 - дерево
- •Алгоритм удаления элемента из 2-3 - дерева
- •Приложение д Алгоритмы операций для хеш-таблицы с открытой адресацией Алгоритм вставки элемента
- •Алгоритм удаления элемента
- •Алгоритм поиска элемента
6.2. Разрешение коллизий и структуры хеш-таблиц
Проблема, свойственная хеш-таблицам, заключается в том, что индексы (хеш-значения) нескольких разных ключей могут совпадать. Эта ситуация неизбежна, поскольку количество возможных значений ключей U значительно превышает размер хеш-таблицы. Такой случай называется коллизией или столкновением.
Существуют два подхода к разрешению коллизий. Во-первых, можно задать структуру хеш-таблицы таким образом, чтобы каждая ячейка таблицы хранила несколько элементов, с одинаковым хеш-значением ключей. Во-вторых, можно найти другую ячейку в хеш-таблице и поместить туда элемент, вступивший в коллизию. В соответствии с этими подходами существуют два типа хеш-таблиц с различной структурой.
Х еш-таблица с цепочками коллизий (рис. 24) представляет собой массив списков коллизий. В позиции i массива хранится указатель на голову списка тех элементов, у которых хеш-значение ключа равно i. Если таких элементов в таблице нет, то позиция i содержит nil. Такой способ разрешения коллизий снимает ограничения на количество элементов, включённых в таблицу.
Рис. 24. Хеш-таблица с цепочками коллизий.
Хеш-таблица с открытой адресацией представляет собой массив Т, в который помещаются элементы таблицы по индексу, равному хеш-значению ключа. При возникновении коллизии выполняется поиск другой ячейки массива (зондирование), в которой может разместиться элемент. Эта ячейка должна быть свободной (открытой). Для пометки состояния в ячейки таблицы вводится специальный признак состояния ячейки state. Ячейка хеш-таблицы может быть в одном из трех состояний: свободна (free), занята (busy), свободна после удаления из неё элемента (deleted). Открытой считается ячейка, находящаяся в состоянии free или deleted.
При поиске свободной ячейки вычисляется и просматривается последовательность индексов ячеек массива. Последовательность зависит от ключа. k и номера попытки поиска i. Для вычисления последовательности к хеш-функции добавляется второй аргумент - номер попытки (нумерация начинается с 0). Последовательность ячеек для некоторого ключа k имеет вид <h (k, 0), h (k, 1), …, h (k, m-1)>.
Обычно используются три способа поиска свободных ячеек, называемых линейное зондирование, квадратичное зондирование и зондирование с двойным хешированием.
При линейном зондировании хеш-функция при каждой попытке поиска вычисляет новую ячейку по формуле
h (k, i) = (h(k) + i) mod m.
, где h(k)- обычная хеш-функция, i – номер попытки поиска свободной ячейки.
При работе с ключом k первой выбирается ячейка T[h(k)], а затем перебираются ячейки таблицы подряд: T[h(k)+1], T[h(k)+2], …. После ячейки T[m-1] следует ячейка T[0]. У метода очевидный недостаток: он может привести к образованию кластеров – длинных последовательностей занятых ячеек, идущих подряд. Это удлиняет поиск.
При квадратичном зондировании хеш-функция, задаёт квадратичную последовательность ячеек по формуле:
h (k, i) = (h (k) + c1 i + c2i2) mod m,
, где c1≠ 0 и c2 ≠ 0 – некоторые константы. Пробы начинаются с ячейки T[h(k)], но дальше ячейки просматриваются не подряд. Номер пробуемой ячейки квадратично зависит от номера попытки. Для того, чтобы использовались все ячейки таблицы, c1 и c2 нужно тщательно подбирать. Хорошую квадратичную последовательность проб даёт алгоритм, явно не использующий константы c1 и c2. Алгоритм продемонстрирован на примере вставки элемента в хеш-таблицу.
Hash_Insert (T, k)
j← h(k)
i← 0
while i ≠ m
do if T[j] = free or deleted
then T[j] = k
return j
i← (i + 1)
j← (j + i) mod m
error “Переполнение хеш-функции”
При квадратичном зондировании тенденция к образованию кластеров частично устраняется. Хотя и остается эффект кластеризации в более мягкой форме – образование вторичных кластеров.
Двойное хеширование – один из лучших методов открытой адресации. Последовательности индексов, возникающие при зондировании с двойным хешированием, близки к равномерному хешированию. При двойном хешировании функция h имеет вид:
h (k, i) = (h(k) + ih1(k)) mod m
, где h(k) и h1 (k) – обычные хеш-функции. То есть, последовательность проб при зондировании является арифметической прогрессией по модулю m с первым членом h(k) и шагом h1(k).
Чтобы последовательность зондируемых ячеек охватывала всю таблицу, значение h1(k) должно быть взаимно простым с размером хеш-таблицы m. Простой способ добиться этого условия – выбрать в качестве m степень двойки, а функцию h1(k) взять такую, чтобы она принимала только нечетные значения. В другом варианте, если m – простое число, значения h1(k) - целые положительные числа, меньшие m. Например, если используется модульное хеширование, в котором размер хеш-таблицы выбирается, как простое число, то можно положить:
h (k) = k mod m
h1(k) = 1 + (k mod m1),
, где m1 чуть меньше, чем m (m1= m-1 или m-2).