Структура данных, которую вы хотите использовать, будет во многом зависеть от вашей схемы доступа. Если запросы очень частые, а операции модификации нечастые, вы можете просто сохранить флаг «грязный» и пересчитать суммы по запросу, если установлен флаг «грязный».
Затем вы можете уточнить это, установив «грязный индекс», который содержит индекс самого низкого элемента, который был изменен. По запросу вы должны пересчитать суммы для этого элемента и все после. Или, возможно, только до элемента, для которого вам нужна сумма, и в этот момент вы можете обновить «грязный индекс».
Такая ленивая оценка может быть очень эффективной, если запросы часты, а модификации редки, или если в шаблоне много модификаций, за которыми следует множество запросов.
'swap' и 'append` можно выполнить за O (1) раз, и они не "испачкают" суммы, если они еще не были испорчены. «замена», конечно, приведет к тому, что грязный индекс будет установлен на этот индекс (при условии, конечно, что он не был уже с более низким индексом).
slidebefore
и slideafter
являются неотъемлемо O (N), если ваша структура данных является массивом, потому что вы должны перемещать данные в массиве. В вашем примере у вас есть:
list == [10, 4, 3, 5, 1]
list.slideBefore(0, 3) // slides 1st position value to before the 2nd position
list == [4, 3, 10, 5, 1]
Таким образом, элементы 1 и 2 в массиве должны были быть сдвинуты влево на одну позицию, чтобы освободить место для позиции 0, которую нужно переместить. Если бы у вас было slideBefore(0, 1000)
, то 1000 элементов в массиве должны были бы переместиться на одну позицию вверх. Если эти операции выполняются часто и у вас большой список, вам, вероятно, понадобится другое базовое представление.
Другая возможность - реализация списка. Представьте себе список из 20 предметов, который разбит на 4 подсписка по 5 предметов в каждом. Каждый подсписок поддерживает количество предметов и сумму предметов в нем. Каждый узел в подсписке поддерживает текущую сумму всех элементов перед ним в списке. Когда вы обновляете элемент, вам нужно только обновить суммы для подсписка этого элемента. Опять же, если вы используете ленивое вычисление, вы будете пересчитывать суммы только для следующих подсписков, если кто-то запрашивает их.
Чтобы обрабатывать вставки и удаления, разрешите подлистам расти до некоторого максимального значения, прежде чем они будут разделены. Скажите, что ваш «идеал» - это пять пунктов в каждом подсписке. Но вы позволяете ему расти до 10, прежде чем разделить его на два подсписка. Для удаления вы можете разрешить подсписку переходить в 0 или объединить его с предыдущим или следующим подсписком, если в подсписке менее 3 элементов.
Идеальный размер подсписков будет зависеть от общего количества элементов, которые вы ожидаете включить в список, и, опять же, от сочетания операций, которые вы ожидаете встретить. Операции, которые по своей природе O (N) (такие как удаление и скольжение), будут отдавать предпочтение меньшим подспискам, но тогда пересчет становится более дорогим, потому что у вас больше подсписков.
Это на самом деле не меняет сложности алгоритма во время выполнения (то есть O (n / 5) по-прежнему считается O (N)), но оно действительно меняет фактическое время выполнения на довольно немного. Для списков среднего размера это может быть настоящей победой.