Алгоритм работает в среднем за время O(n), исходя из того, что поиск хэша в среднем занимает O(1).
Есть две детали реализации. Во-первых, последний шаг, который, кажется, включает в себя прикосновение к каждому значению в H:
- уменьшить все значения в H на 1
Чтобы сделать это O (1), мы добавляем одно дополнительное место хранения, называемое base
, которое инициализируется равным 0. Затем мы модифицируем алгоритм следующим образом:
while an item i from the input stream arrives:
if H[i] exists
increment the value associated with H[i]
elsif number of items in H < k
put H[i] into map with value of base + 1
elseif there exists an entry j with a value of base
remove j and put H[i] into map with value of base + 1
else
increment base
endwhile
Вторая проблема — найти запись со значением base
(или 0) в O(1). Это можно сделать, сохранив элементы в виде «гребенки»: связанного списка из двусвязных списков. Каждый внутренний связанный список содержит записи с определенным количеством. Внешний связанный список содержит списки счетчиков, упорядоченные по количеству, причем заголовок указывает на список с наименьшим количеством. Если вы нарисуете эту структуру данных, она будет выглядеть как гребенка:
[ base ] -> entry a -> entry b -> entry c
|
[ base + i ] -> entry d
|
[ base + j ] -> entry e -> entry f
|
etc.
Хэш-таблица теперь указывает на записи, а не содержит их. Чтобы увеличить количество одной записи, запись удаляется из ее списка (если список содержит более одного элемента) и либо вставляется в следующий список, либо помещается в одноэлементный список, который вставляется после списка, в котором она находилась. , в зависимости от количества, связанного со следующим списком. Эта операция O (1).
Структура данных гребенки по-прежнему равна O(k), где k — количество элементов в хеше, потому что не может быть больше различных счетчиков, чем элементов.
Вместо двусвязных списков можно использовать простой массив и список индексов первой записи с каждым счетчиком. Чтобы переместить запись в следующее ведро подсчета, вы начинаете с того, что заменяете ее последней записью с этим подсчетом, а затем либо продвигаете начало следующего списка подсчета, либо вставляете новую запись списка подсчета, в зависимости от того, является ли подсчет следующего списка подсчета. один больший или более чем один больший. Чтобы выполнить замену, необходимо обновить расположение двух замененных записей в хэше, но это все еще O (1).
person
rici
schedule
16.06.2016