List order maintenance — различия между версиями
(page created) |
(half of proof added) |
||
Строка 1: | Строка 1: | ||
− | [[Файл:ListABCD.jpg| | + | [[Файл:ListABCD.jpg|250px|thumb|right|Для такого списка команда <tex>\mathrm{order(D,B)}</tex> выдаст <tex>\mathrm{false}</tex>.]] |
'''List order maintance''' {{---}} проблема поддержки списка со следующими командами: | '''List order maintance''' {{---}} проблема поддержки списка со следующими командами: | ||
* <tex>\mathrm{insert(p, q)}</tex> {{---}} вставка нового элемента <tex>p</tex> в список сразу после <tex>q</tex>; | * <tex>\mathrm{insert(p, q)}</tex> {{---}} вставка нового элемента <tex>p</tex> в список сразу после <tex>q</tex>; | ||
Строка 5: | Строка 5: | ||
* <tex>\mathrm{order(p, q)}</tex> {{---}} команда, возвращающая <tex>\mathrm{true}</tex> , если <tex>p</tex> в списке находится до <tex>q</tex> и <tex>\mathrm{false}</tex> иначе. | * <tex>\mathrm{order(p, q)}</tex> {{---}} команда, возвращающая <tex>\mathrm{true}</tex> , если <tex>p</tex> в списке находится до <tex>q</tex> и <tex>\mathrm{false}</tex> иначе. | ||
− | + | Структура данных подходит и для односвязных, и для двусвязных списков. | |
== Алгоритм == | == Алгоритм == | ||
− | + | [[Файл:ListABCDwithMarks.jpg|250px|thumb|right|Пример расставления меток для списка, <tex>u=3</tex>.]] | |
+ | Рассмотрим реализацию списка с командой порядка, где все операции выполняются за амортизационную <tex>O(1)</tex>. | ||
+ | Дадим каждому элементу списка метки длины <tex>u</tex> из битов. Пусть <tex>u:\dfrac{n}{2}<2^u \leqslant 2n</tex>, где <tex>n</tex> - количество элементов в списке. Если после добавления или удаления элементов <tex>u</tex> перестанет удовлетворять неравенству, пересчитаем все метки заново. Пересчет меток занимает амортизационно <tex>O(1)</tex> по аналогии с саморасширяющимся массивом. Пусть метки идут по возрастанию от начала к концу списка, тогда операцию <tex>\mathrm{order(p,q)}</tex> можно сделать, сравнив метки за <tex>O(1)</tex>. Теперь опишем взаимодействие с метками при выполнении других команд. | ||
+ | Для выполнения <tex>\mathrm{remove(p)}</tex> просто удалим элемент <tex>p</tex> вместе с его меткой, проверим, удовлетворяет ли <tex>u</tex> неравенству, если нет - пересчитаем. Для <tex>\mathrm{insert(p,q)}</tex> существуют два возможных случая: | ||
+ | Метка <tex>r</tex>: <tex>\mathrm{r.label}>\mathrm{p.label} + 1</tex>, где <tex>r</tex> {{---}} следующий за <tex>p</tex> элемент в списке. Тогда между метками <tex>p</tex> и <tex>r</tex> есть свободная метка, которую мы дадим <tex>q:\mathrm{q.label} = \dfrac{\mathrm{p.label}+\mathrm{r.label}}{2}</tex>. После этого опять проверим <tex>u</tex> на соответствие неравенству. | ||
+ | [[Файл:UBitTreeListABCD.jpg|350px|thumb|right|Точка - метка в листе используется.]] | ||
+ | |||
+ | В случае, если между метками <tex>p</tex> и <tex>q</tex> свободной метки нет, нам придется пересчитать метки следующим образом. Построим виртуальное дерево отрезков над всеми возможными метками, где в каждом узле будем хранить <tex>1</tex> бит. В листьях будем хранить, используется ли уже эта метка. Пусть <tex>\mathrm{weight(x)}</tex> {{---}} это количество помеченных (используемых) листьев (меток) в поддереве <tex>x</tex>, а <tex>\mathrm{size(x)}</tex> {{---}} это количество всех листьев в поддереве <tex>x</tex>. Для любой <tex>1<\alpha<2</tex> будем считать, что узел дерева переполнен, если <tex>\dfrac{\mathrm{weight(x)}}{\mathrm{size(x)}}>\dfrac{1}{\alpha^{\mathrm{height(x)}}}</tex>. Именно наличие переполненности мы и будем хранить во всех нелистовых узлах. Стоит заметить, что все листья всегда непереполнены. В худшем случае: <tex> \dfrac{\mathrm{weight(leave)}}{\mathrm{size(leave)}} = 1 = \dfrac{1}{\alpha^{\mathrm{height(x)}}} = \dfrac{1}{\alpha^{0}}</tex>. Получается, что чем выше, тем более разреженными должны быть поддеревья непереполненных узлов. | ||
+ | [[Файл:UBitTreeExample.jpg|350px|thumb|right|Пример виртуального дерева отрезков над метками, где узел с крестиком - переполненный узел, а с галочкой - не переполненный.]] | ||
+ | |||
+ | Тогда, как только мы получаем команду вставить элемент элемент, которому не хватает метки, мы поднимаемся вверх от метки элемента <tex>p</tex>, пока не найдем первый непереполненный узел. Может случиться такое, что на всем пути до корня мы не найдем ни одного непереполненного узла. Чтобы этого избежать, изменим требования к <tex>u</tex> позже. Как только мы нашли первый непереполненный узел, переназначим метки в его поддереве так, чтобы они находились друг от друга на одинаковых расстояниях (места точно хватит, так как <tex>\dfrac{\mathrm{weight(x)}}{size(x)}\leqslant\dfrac{1}{\alpha^{\mathrm{height(x)}}}</tex>, если узел непереполненный). После этого плотность распределения всех занятых листьев составит примерно <tex>\dfrac{1}{\alpha^{\mathrm{height(x)}}}</tex>. | ||
+ | |||
+ | Тогда чтобы после перераспределения меток в поддереве узла <tex>x</tex>, мы заново пришли к этому же узлу за перераспределением, нужно, чтобы его сын снова переполнился. Если <tex>y</tex> {{---}} сын <tex>x</tex>, то он переполнится, когда <tex>\dfrac{\mathrm{weight(y)}}{\mathrm{size(y)}}>\dfrac{1}{\alpha^{\mathrm{height(x) - 1}}}</tex>. Чтобы это произошло, требуется, чтобы было сделано еще <tex>\mathrm{size(y)}*(\dfrac{1}{\alpha^{\mathrm{height(x) - 1}}} - \dfrac{1}{\alpha^{\mathrm{height(x)}}}) = \mathrm{size(y)}*\dfrac{\alpha - 1}{\alpha^{\mathrm{height(x)}}}</tex> добавлений. | ||
+ | |||
+ | == Применение == | ||
== См. также== | == См. также== |
Версия 20:11, 13 марта 2016
List order maintance — проблема поддержки списка со следующими командами:
- — вставка нового элемента в список сразу после ;
- — удаление элемента из списка;
- — команда, возвращающая , если в списке находится до и иначе.
Структура данных подходит и для односвязных, и для двусвязных списков.
Содержание
Алгоритм
Рассмотрим реализацию списка с командой порядка, где все операции выполняются за амортизационную
. Дадим каждому элементу списка метки длины из битов. Пусть , где - количество элементов в списке. Если после добавления или удаления элементов перестанет удовлетворять неравенству, пересчитаем все метки заново. Пересчет меток занимает амортизационно по аналогии с саморасширяющимся массивом. Пусть метки идут по возрастанию от начала к концу списка, тогда операцию можно сделать, сравнив метки за . Теперь опишем взаимодействие с метками при выполнении других команд.Для выполнения
просто удалим элемент вместе с его меткой, проверим, удовлетворяет ли неравенству, если нет - пересчитаем. Для существуют два возможных случая:Метка
: , где — следующий за элемент в списке. Тогда между метками и есть свободная метка, которую мы дадим . После этого опять проверим на соответствие неравенству.В случае, если между метками
и свободной метки нет, нам придется пересчитать метки следующим образом. Построим виртуальное дерево отрезков над всеми возможными метками, где в каждом узле будем хранить бит. В листьях будем хранить, используется ли уже эта метка. Пусть — это количество помеченных (используемых) листьев (меток) в поддереве , а — это количество всех листьев в поддереве . Для любой будем считать, что узел дерева переполнен, если . Именно наличие переполненности мы и будем хранить во всех нелистовых узлах. Стоит заметить, что все листья всегда непереполнены. В худшем случае: . Получается, что чем выше, тем более разреженными должны быть поддеревья непереполненных узлов.Тогда, как только мы получаем команду вставить элемент элемент, которому не хватает метки, мы поднимаемся вверх от метки элемента
, пока не найдем первый непереполненный узел. Может случиться такое, что на всем пути до корня мы не найдем ни одного непереполненного узла. Чтобы этого избежать, изменим требования к позже. Как только мы нашли первый непереполненный узел, переназначим метки в его поддереве так, чтобы они находились друг от друга на одинаковых расстояниях (места точно хватит, так как , если узел непереполненный). После этого плотность распределения всех занятых листьев составит примерно .Тогда чтобы после перераспределения меток в поддереве узла
, мы заново пришли к этому же узлу за перераспределением, нужно, чтобы его сын снова переполнился. Если — сын , то он переполнится, когда . Чтобы это произошло, требуется, чтобы было сделано еще добавлений.