Есть ли способ эффективно восстановить коллекцию на основе последовательности вставок/абзацев?

Примечание: приведенный ниже код является С#, но на самом деле ответ на любом языке был бы полезен для меня.

Предположим, что вместо фактической коллекции (например, a List<T>) у меня есть последовательность операций, каждая из которых выглядит примерно так:

struct ListOperation<T>
{
    public enum OperationType { Insert, Remove }

    public OperationType Type;
    public T Element; // irrelevant for OperationType.Remove
    public int Index;
}

Есть ли способ эффективно "восстановить" коллекцию на основе последовательности таких операций?

В частности, я стараюсь избежать очевидной (неэффективной) реализации, в основном, просто создавая операции List<T> и вызывающие Insert и RemoveAt -both O (N) для каждого элемента.


Обновить. Скажем, "последовательность" операций на самом деле представляет собой конкретный набор, счет которого известен и который случайно доступен по индексу (так, например, a ListOperation<T>[]). Пусть также говорят, что фактический подсчет результирующей коллекции уже известен (но на самом деле это было бы тривиально, чтобы выяснить в O (N) в любом случае, путем подсчета вставок и абзацев). Любые другие идеи?

Ответы

Ответ 1

Я думаю, вы можете сделать это в O (n lg n), используя индексированное сбалансированное двоичное дерево (двоичное дерево, где каждый node хранит количество узлов слева и справа). С помощью этой структуры вы можете получить вложение или удаление O (lg n) в худшем случае в любом месте, пройдя дерево, чтобы найти позицию, в которой находится новый элемент, а затем выполнить любое исправление, необходимое для поддержания условия баланса (например, если это красно-черное дерево, вы бы сделали красно-черное дерево fixup).

Учитывая эту настройку, вы можете переименовать все операции в такую ​​древовидную структуру, как это в O (n lg n), потому что каждая отдельная операция занимает не более O (lg n) для завершения. После того, как у вас есть дерево, вы можете выполнить обход элементов по порядку, чтобы вернуть их в правильном порядке, и можете добавлять все значения в результирующий список в O (n) раз, для сети O (n lg п).

Я подумаю об этом больше и посмотрю, смогу ли я придумать способ сделать это в линейном времени. Тем временем это, по крайней мере, показывает, что это возможно сделать в субквадратичное время.

Ответ 2

У меня есть подозрение, что может быть алгоритм O (n).

Шаг 1:

Radix-сортировка в цифровом виде по индексу. Принимает время O (n). Это стабильный вид, если он сделан со стороны LSB.

Шаг 2:

Скажем, есть операции с индексом i, но нет операций с меньшим индексом, который не был выполнен. Затем мы можем воспроизвести операции с индексом я в правильном порядке. То, что делают операции "вставить" и "удалить", не ясно для меня. Худший случай - O (n lg n) с идеями бинарного дерева, но, возможно, повторение может быть выполнено в O (n), поскольку оно локально.

Шаг 3:

Поднимите шаг 2 до индуктивного аргумента как доказательство правильности. После шагов в индексе я существует поддерживаемый инвариант и более короткий список операций, поэтому по индукции... (подробнее)...