Лекция 4 Элементарные структуры данных часть 2
TRANSCRIPT
Анализ комбинаторных алгоритмов
Лекция №4Элементарные
структуры данныхЧасть II
Хеш-таблицы. Общие понятия.
В случае, если требуется динамическая структура данных, поддерживающая лишь словарные операции, часто пользуются методом хеширования.
Хеш-таблица – это обобщение обычного массива с расширенной адресацией, использующее понятие хеш-функции.
Хеш-таблицы. Прямая адресация.
Пусть возможными ключами являются числа 0, 1, … m-1 (число m не велико). Пусть ключи каждого элемента динамического множества различны.
Для хранения динамического множества можно использовать массив T[0..m-1], называемый таблицей с прямой адресацией, каждая позиция (ячейка) которого соответствует определенному ключу.
Хеш-таблицы. Прямая адресация.
1
90
23
5 8
4
76
U
K
nil 0
nil 12
3
nil 4
5
nil 6
nil 7
8
nil 9
void DirectInsert(T, x){T[x->key] = x;
}
void DirectDelete(T, x){T[x->key] = null;
}
void DirectSearch(T, key){return T[key];
}
Хеш-таблицы. Прямая адресация.
Прямая адресация не применима: если число возможных ключей большое если ключи не являются ограниченными
упорядоченными целыми числами.
Кроме того, если число реально присутствующихключей k мало по сравнению с общимколичеством возможных ключей много памятитратится зря.
Хеш-таблицы. Коллизии.
Для того чтобы решить возникшие проблемы, можно выделить массив относительно небольшого размера, и сопоставить каждому значению ключа ячейку с номером h(k), где k – значение ключа, h – некоторая функция, называемая хеш-функцией.
Проблема в этом случае состоит в том, что для двух разных ключей значения хеш-функции могут совпасть. В таком случае говорят, что случилась коллизия.
Хеш-таблицы. Коллизии.
Одним из способов решения проблемы коллизий в хеш-таблицах является технология сцепления элементов. Идея состоит в том, чтобы элементы, имеющие одно и то же хеш-значение, связывать в список.
При равномерном хешировании (вероятность попадания элемента в ячейку с некоторым номером не зависит от того, куда попали другие элементы), время поиска элемента в хеш-таблице составляет
где n – кол-во внесенных элементов, m – размер хеш-таблицы.m
n ),1(
Хеш-таблицы. Коллизии.
nil 0
nil 1
2
3
nil 4
nil 5
nil 6
nil 7
8
nil 9
k1k2
k3
k5 k8k4
k7k6
U
K
nilK6
K7
nilK5K8
nilK1K4
Хеш-таблицы. Коллизии.
void ChanedHashInsert(T, x){ListInsert(T[ h(x->key) ], x);
}
void ChanedHashDelete(T, x){ ListDelete(T[ h(x->key) ], x);
}
void Chaned_Hash_Search(T, key){ return ListSearch(T[ h(k) ], k);
}
Хеш-таблицы. Выбор хеш-функции.
Идеальной хеш-функцией, является такая функция, при использовании которой коллизии не случаются, если размер хеш-таблицы больше количества внесенных элементов.
При отсутствии информации о характере вносимых элементов выбор идеальной функции невозможен.
Хорошая хеш-функция должна хорошо «переме-шивать» ключи по ячейкам.
Хеш-функция должна оставаться детермини-рованной.
Хеш-таблицы. Выбор хеш-функции.
При построении хеш-функции предполагают, что ее область определения – целые неотрицательные числа.
Если ключи не являются целыми неотрица-тельными числами, их к ним приводят. Например, строку можно привести к числу с помощью ASCII кода: “ppt” = 255*255*112+255*112+116 = 7311476
Хеш-таблицы. Выбор хеш-функции.
Наиболее распространенные методы построенияхеш-функции: Деление с остатком: h(k) = k mod m, где m –
размер хеш-таблицы. Умножение: , A-некоторая
константа в интервале (0..1), kA mod 1 – дробная часть произведения kA, m – размер хеш-таблицы. Пример удачного значения константы A=0.6180339887…
)1mod()( kAmkh
Хеш-таблицы. Выбор хеш-функции.
Если знать заранее хеш-функцию h, то можно специально подобрать данные так, что все n ключей попадут в одну ячейку.
Единственный выход из такого положения – случайно выбирать хеш-функцию из некоторого множества во время исполнения программы. Такой подход называется универсальным хешированием, множество хеш-функций – универсальным семейством.
Хеш-таблицы. Выбор хеш-функции.
Семейство H является универсальным, если для любых двух ключей x,y число функций для которых h(x)=h(y) равно |H|/m.
Построить универсальное семейство можно так:
при условии что m – простое число > max(«байт»)x= <x0,x1,…xr>, где xi – «байт» числа xa= <a0,a1,…ar>, где a – последовательность, с элементами, принадлежащими множеству [0..m-1].
Hh
1 ,}{ ,mod)(0
rHhHmxaxhr
i aaiia
Хеш-таблицы. Открытая адресация.
Открытая адресация – еще один способ решения проблемы коллизий в хеш-таблице.
При открытой адресации списков нет, все элементы хранятся в ячейках хеш-таблицы.
Таким образом количество внесенных элементов не может быть больше размера таблицы, т.е. коэффициент заполнения всегда меньше 1.
Хеш-таблицы. Открытая адресация.
При открытой адресации, добавляя новый элемент, мы просматриваем таблицу пока не встретим свободное место.
Если просматривать таблицу последовательно, скорость работы алгоритма составит O(n).
Идея состоит в том, что порядок просмотра таблицы должен зависеть от ключа.
Хеш-функция при открытой адресации получает второй параметр – номер попытки.
Хеш-таблицы. Открытая адресация.
nil 0
nil 12
3
4
5
nil 6
nil 7
8
9
k1k2
k3
k5 k8k4
k7k6
U
K
Последовательность испробованных мест <h(k,0),h(k,1),..h(k,m-1)> называется последова-тельностью проб для заданного ключа k.
Хеш-функция h(k,i) должна быть такой, что каждое из чисел 0..m-1 должно появиться в последовательности проб ровно 1 раз.
Хеш-таблицы. Открытая адресация.void OpenHashInsert(T,x){
i=0;do{ j=h(x->key,i);
if(T[j]==null){T[j] = x; return;}; i++;}while(i<m); error(“переполнение таблицы”);}
void OpenHashDelete(T,x){i=0;do{ j=h(x->key,i); if(T[j]!=null){ if(T[j]->key == x->key){ T[j]=null; return;} }; i++;}while((i<m)&&(T[j]!=null))
}
void OpenHashSearch(T,key){i=0;do{ j=h(k,i);if(T[j]!=null){if(T[j]->key == key)return T[j]}; i++;}while((i<m)&&(T[j]!=null))return null;}
Хеш-таблицы. Открытая адресация.Наиболее распространенные методы построенияхеш-функции при открытой адресации: Линейная последовательность проб:
h(k,i) = (h`(k)+i) mod m, т.е. для ключа k начинают с ячейкиномер h`(k), а затем перебирают ячейки подряд.
Квадратичная последовательность проб:h(k,i) = (h`(k)+с1*i+ с2*i2) mod m, здесь h`(k) обычная хеш-функция, c1 и c2-константы.
Двойное хеширование:h(k,i) = (h1`(k)+i*h2`(k)) mod m, здесь h1`(k) и h2`(k) обычныехеш-функции, т.е. просматриваются ячейки начиная с h1`(k) cшагом h2`(k)
Хеш-таблицы. Открытая адресация.
При равномерном хешировании в случае с открытой адресацией все перестановки множества {0..m-1} равновероятны. Скорость поиска при равномерном хешировании составляет
Хуже всего работает линейная последовательность проб – она может привести к образованию кластеров
Двойное хеширование - один из лучших методов открытой адресации.
заполнения ткоэффициенmn где ),
11ln1(
Применимость хеш-таблиц.
Главное достоинство хеш-таблиц – высокая скорость поиска информации. Главный недостаток – большой расход памяти.
Хеш-таблицы следует применять только при условии что количество сохраняемых элементов сравнимо с ее размером.
Если количество сохраняемых элементов меньше размера таблицы лучше применять хеширование с открытой адресацией.
Древовидные структуры данных
Корневое дерево – ациклический неориентированный граф, в котором одна из вершин выделена и называется корнем.
Пусть x-произвольная вершина дерева с корнем r. Существует единственный путь из r в x, причем все вершины на этом пути называются предками x. Если y предок x, то x потомок y.
Для каждой вершины x в дереве можно рассмотреть поддерево с корнем x.
Если <x,y> - последнее ребро на пути из корня в x, то y – родитель x, x – ребенок y.
Вершины имеющие общего родителя - братья. Вершина не имеющая детей – лист, все остальные – внутренние. Число детей у вершины – ее степень. Длина пути от корня до вершины – глубина вершины. Максимальная глубина – высота дерева.
Древовидные структуры данных
7
1 3
5 2 4
6 9
8 10
11
Братья
Корень
Высотадерева
Родитель Ребенок
Поддерево с корнем 1
Листья
Древовидные структуры данных.Способ «Родитель-дети».
Если заранее известно, что степень вершины корневого дерева ограничена константой k, то часто применяют способ реализации древовидной динамической структуры «родитель-дети».
Идея состоит в том, что каждую вершину можно представить как структуру, помимо ключа и дополнительной информации хранящую ссылку на вершину-родитель и k ссылок на вершины-дети.
Древовидные структуры данных.Способ «Родитель-дети».
/
/
/ / / /
/ / / // / / // / / /
/ / / /
/ / / /
Древовидные структуры данных.Способ «Левый ребенок – правый брат».
Если изначально k не известно, а также в целях экономии памяти, можно использовать способ «левый ребенок – правый брат».
В этом случае вершина дополнительно хранит три ссылки: на своего родителя, на самого левого своего ребенка, на своего правого брата.
Древовидные структуры данных.Способ «Левый ребенок – правый брат».
/
/
//
/ /
/ / / / /