Добавил:
Upload Опубликованный материал нарушает ваши авторские права? Сообщите нам.
Вуз: Предмет: Файл:
algorithms.doc
Скачиваний:
29
Добавлен:
06.12.2018
Размер:
9.73 Mб
Скачать
  1. Лекция 12

  1. Хеширование

Фактически, алгоритмы работы со всеми структурами данных, связанными с деревьями, основаны на операции сравнения. Можно использовать другой подход. Попробуем на основе значения элемента x, заносимого в структуру данных, вычислять некоторую функцию h(x), которая будет так или иначе отражать положение элемента x в структуре данных (например, индекс элемента в массиве). Такая функция называется хэш-функцией. Сама структура данных, поиск элементов в которой использует хэш-функцию, называется хэшируемой.

Наиболее прямолинейным способом хранения хэшируемых данных является массив массивов элементов. Т.е. для каждого значения хэш-функции отводится свой массив, в котором хранятся элементы, рассматриваемого типа. Например, для работы с множеством целых чисел, при использовании хэш-функции h(x) со значениями 0h(x)<M, можно использовать массивы

int h_array[M][N], l_array[M];

Здесь константа N задает ограничение на количество чисел, содержащихся в структуре данных, для каждого значения хэш-функции. Данные, соответствующие значению хэш-функции h(x)=i, хранятся в массиве h_array[i], количество элементов в этом массиве хранится в переменной l_array[i].

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

    1. Метод многих списков

Модификацией вышеописанного алгоритма является алгоритм, хранящий данные методом многих списков. В нем каждому значению хэш-функции сопоставляется свой список значений, содержащий хранимые данные. В этом случае на языке С при использовании стандартных списков (L1 или L2) для организации данных следует завести массив указателей на вершину списка:

CList *h_list[M];

здесь M – (как и выше) константа, ограничивающая максимальное значение хэш-функции; CList – тип переменной для хранения одной вершины списка.

Инициализация структуры данных тривиальна:

void Init(Clist *h_list[]){memset(h_list,0,M*sizeof(Clist*));}

Можно оценить среднее время поиска элемента в такой структуре данных в ситуации, когда у нас используется `идеальная’ хэш-функция, т.е. время ее работы равно O(1) и она с равной вероятностью выдает все свои значения для потока входных данных. В этом случае среднее время поиска элемента пропорционально среднему количеству элементов в произвольном списке из массива h_list.

Итак, пусть у нас хранится всего N элементов в M списках. Вероятность попадания элемента в один определенный список равна p=1/M. Тогда вероятность попадания k элементов в один конкретный список равна pk=CNkpk(1-p)N-k. Средняя длина списка равна

lN =k=0kN k pk = Np = N/M

Данная формула доказывается следующим образом:

(x-q)N=k=0kN CNkxk q N-k ; продифференцируем по x:

(x-q)N= N(x-q)N-1 = k=0kN k CNkxk-1 q N-k

Теперь, если взять x=p, q=1-p, то получим

lN =k=0kN k pk = p N(p- (1-p))N-1=Np

Т.о., мы доказали следующую теорему

Теорема. Если хэш-функция h(x) с равной вероятностью принимает все свои значения 0h(x)<M, то среднее время поиска, добавления, удаления элемента в хэшируемом множестве, реализованном с помощью метода многих списков,

TN,M = (N/M).

В худшем случае для поиска, добавления, удаления элемента требуется время, равное (N).

Для случая хэширования с помощью массивов оценки аналогичны.

Соседние файлы в предмете [НЕСОРТИРОВАННОЕ]