Список с пропусками — различия между версиями
Kamensky (обсуждение | вклад) м (Пояснил, почему 2logn) |
Kamensky (обсуждение | вклад) м (Увеличены дроби) |
||
Строка 14: | Строка 14: | ||
Тогда время работы алгоритма поиска будет зависеть от количества элементов на уровне <tex>L_2</tex>. Представим, что на этот уровень у нас случайным образом попало несколько элементов. Следовательно в худшем случае поиска мы получим следующую оценку на время работы: | Тогда время работы алгоритма поиска будет зависеть от количества элементов на уровне <tex>L_2</tex>. Представим, что на этот уровень у нас случайным образом попало несколько элементов. Следовательно в худшем случае поиска мы получим следующую оценку на время работы: | ||
− | <tex> \approx \vert L_2\vert + \ | + | <tex> \approx \vert L_2\vert + \dfrac{\vert L_1\vert }{\vert L_2\vert } = \vert L_2\vert + \dfrac{n}{\vert L_2\vert }</tex> |
Минимизируя, мы получаем, что <tex>\vert L_2 \vert ^ 2 = n</tex> | Минимизируя, мы получаем, что <tex>\vert L_2 \vert ^ 2 = n</tex> | ||
Строка 20: | Строка 20: | ||
В итоге время за которое мы найдем элемент в списке с пропусками с двумя уровнями будет равняться: | В итоге время за которое мы найдем элемент в списке с пропусками с двумя уровнями будет равняться: | ||
− | <tex> \sqrt{n} + \ | + | <tex> \sqrt{n} + \dfrac{n}{\sqrt{n}} = 2 \sqrt{n}</tex> |
Делая аналогичные подсчеты для списков с пропусками, в которых содержится больше уровней, получаем: | Делая аналогичные подсчеты для списков с пропусками, в которых содержится больше уровней, получаем: | ||
Строка 37: | Строка 37: | ||
# Повторять предыдущий шаг до тех пор, пока у нас «подброс монетки» дает положительный результат | # Повторять предыдущий шаг до тех пор, пока у нас «подброс монетки» дает положительный результат | ||
− | Таким образом, если использовать честную монету, то математическое ожидание количества элементов на втором уровне равняется <tex>\ | + | Таким образом, если использовать честную монету, то математическое ожидание количества элементов на втором уровне равняется <tex>\dfrac{n}{2}</tex>, на третьем уровне <tex>\dfrac{n}{4}</tex> и т.д. На уровне <tex>\log{n}</tex> у нас окажется один элемент. Ну и соответственно вероятности попасть элементу на второй уровень — это <tex>\dfrac{1}{2}</tex>, на третий <tex>\dfrac{1}{4}</tex> и т.д. Вероятность попасть на уровень <tex>\log{n}</tex> равна <tex>\dfrac{1}{n}</tex> |
===Удаление элемента=== | ===Удаление элемента=== | ||
Строка 58: | Строка 58: | ||
SkipNode x = header | SkipNode x = header | ||
for i = level to 0 | for i = level to 0 | ||
− | + | while x.forward[i] != NULL and x.forward[i].value < key | |
− | + | x = x.forward[i] | |
− | + | x = x.forward[0] | |
return x != NULL && x.value == key | return x != NULL && x.value == key | ||
− | + | ||
− | + | ||
− | + | ||
void Insert(int value) | void Insert(int value) | ||
Строка 70: | Строка 70: | ||
SkipNode update | SkipNode update | ||
update.assign(MAX_LEVEL + 1, 0) | update.assign(MAX_LEVEL + 1, 0) | ||
− | + | ||
for i = level to 0 | for i = level to 0 | ||
− | + | while x.forward[i] != NULL and x.forward[i].value < value | |
− | + | x = x.forward[i] | |
− | + | update[i] = x | |
x = x.forward[0] | x = x.forward[0] | ||
if x == NULL or x.value != value | if x == NULL or x.value != value | ||
int lvl = random_level() | int lvl = random_level() | ||
if lvl > level | if lvl > level | ||
− | + | for i = level + 1 to lvl | |
− | + | update[i] = header | |
− | + | level = lvl | |
x = new SkipNode(lvl, value) | x = new SkipNode(lvl, value) | ||
− | + | for i = 0 to lvl | |
− | + | x.forward[i] = update[i].forward[i] | |
− | + | update[i].forward[i] = x | |
− | + | ||
− | + | ||
void Erase(int value) | void Erase(int value) | ||
SkipNode x = header | SkipNode x = header | ||
SkipNode update | SkipNode update | ||
− | + | update.assign(MAX_LEVEL + 1, 0) | |
for i = level to 0 | for i = level to 0 | ||
− | + | while x.forward[i] != NULL and x.forward[i].value < value | |
− | + | x = x.forward[i] | |
− | + | update[i] = x | |
− | + | x = x.forward[0] | |
if x.value == value | if x.value == value | ||
for i = 0 to level | for i = 0 to level | ||
− | + | if update[i].forward[i] != x | |
− | + | break | |
− | + | update[i].forward[i] = x.forward[i]; | |
delete x | delete x | ||
while level > 0 or header.forward[level] == NULL | while level > 0 or header.forward[level] == NULL | ||
− | + | level-- | |
Версия 03:40, 16 июня 2014
Список с пропусками (skip-list) — одна из вероятностных структур данных, на ряде параллельных отсортированных связных списков с эффективностью, сравнимой с бинарными деревьями поиска. Все операции со списком с пропусками осуществляются за
с большой вероятностью.Отсортированный связный список является простейшей структурой с временем поиска
. Одним из способов улучшить асимптотику данной структуры является добавление дополнительного уровня, обеспечивающего быстрый доступ через несколько элементов.Содержание
Операции над структурой
Поиск элемента
Допустим, что в нашем списке с пропусками существуют два уровня:
, в котором содержатся все элементы и , в котором присутствует только часть из них. Между одинаковыми элементами этих двух списков существуют ссылки.В таком случае алгоритм поиска в этой структуре будет представлять из себя следующие операции:
- Начинаем поиск элемента в верхнем левом углу
- Передвигаться будем по списку , пока значение в следующей ячейке меньше или равно ключу
- Переместиться в нижний уровень и продолжить аналогичный метод поиска по списку
Тогда время работы алгоритма поиска будет зависеть от количества элементов на уровне
. Представим, что на этот уровень у нас случайным образом попало несколько элементов. Следовательно в худшем случае поиска мы получим следующую оценку на время работы:
Минимизируя, мы получаем, что
В итоге время за которое мы найдем элемент в списке с пропусками с двумя уровнями будет равняться:
Делая аналогичные подсчеты для списков с пропусками, в которых содержится больше уровней, получаем:
- Для трех уровней:
- Для четырех уровней:
- Для пяти уровней:
- Для уровней:
В списках с пропусками, в которых содержится
уровней будет себя вести очень похоже на сбалансированные бинарные деревья поиска. В идеальной данной структуре соотношение между соседними уровнями будет равняться двум. Поиск в списке с пропусками будет осуществляться за асимптотическое время .Вставка элемента
Для вставки элемента в список с пропусками, нам необходимо выполнить следующие шаги:
- Найти с помощью алгоритма поиска позицию, куда нам надо вставить этот элемент
- Вставить наш элемент в нижний уровень списка с пропусками
- «Подбросить монетку» и в зависимости от результата протолкнуть элемент на уровень выше
- Повторять предыдущий шаг до тех пор, пока у нас «подброс монетки» дает положительный результат
Таким образом, если использовать честную монету, то математическое ожидание количества элементов на втором уровне равняется
, на третьем уровне и т.д. На уровне у нас окажется один элемент. Ну и соответственно вероятности попасть элементу на второй уровень — это , на третий и т.д. Вероятность попасть на уровень равнаУдаление элемента
Алгоритм удаления достаточно тривиален.
- Найти удаляемый элемент
- Удалить его со всех уровней
Псевдокод
const float P = 0.5 int random_level() int lvl = (int)(log(frand())/log(1.-P)) if lvl < MAX_LEVEL return lvl return MAX_LEVEL boolean Find (int key) SkipNode x = header for i = level to 0 while x.forward[i] != NULL and x.forward[i].value < key x = x.forward[i] x = x.forward[0] return x != NULL && x.value == key void Insert(int value) SkipNode x = header SkipNode update update.assign(MAX_LEVEL + 1, 0) for i = level to 0 while x.forward[i] != NULL and x.forward[i].value < value x = x.forward[i] update[i] = x x = x.forward[0] if x == NULL or x.value != value int lvl = random_level() if lvl > level for i = level + 1 to lvl update[i] = header level = lvl x = new SkipNode(lvl, value) for i = 0 to lvl x.forward[i] = update[i].forward[i] update[i].forward[i] = x void Erase(int value) SkipNode x = header SkipNode update update.assign(MAX_LEVEL + 1, 0) for i = level to 0 while x.forward[i] != NULL and x.forward[i].value < value x = x.forward[i] update[i] = x x = x.forward[0] if x.value == value for i = 0 to level if update[i].forward[i] != x break update[i].forward[i] = x.forward[i]; delete x while level > 0 or header.forward[level] == NULL level--