Подтвердить что ты не робот

Структура данных, поддерживающая O (1) случайный доступ и наихудший вариант O (1)?

Я понимаю изменчивую индексированную коллекцию, которая использует массив для хранения своих элементов (например, List<T> в .NET или ArrayList в Java) имеет амортизируется O (1) время вставки в конце коллекции. Но тогда всегда есть одна надоедливая вставка на критических стыках, где коллекция только что достигла своей емкости, а следующая вставка требует полной копии всех элементов во внутреннем массиве к новому (предположительно вдвое больше).

Общей ошибкой (по моему мнению) является переход со связанным списком, чтобы "исправить" эту проблему; но я считаю, что накладные расходы на выделение node для каждого элемента могут быть довольно расточительными и на самом деле будут затмевать преимущество гарантированной установки O (1) в том редком случае, что вставка массива является дорогостоящей, каждая другая вставка массива значительно дешевле (и быстрее).

То, что я думал, может иметь смысл - это гибридный подход, состоящий из связанного списка массивов, где каждый раз, когда текущий массив "head" достигает своей емкости, в связанный список добавляется новый массив, вдвое больший. Тогда никакой копии не потребуется, поскольку связанный список все равно будет иметь исходный массив. По существу это похоже на мой подход к List<T> или ArrayList, за исключением того, что, когда бы вы ранее не понесли затраты на копирование всех элементов внутреннего массива, здесь вы берете на себя только расходы на выделение нового массив плюс одна вставка node.

Конечно, это усложняло бы другие функции, если бы они были желательными (например, вставка/удаление в/из середины коллекции); но, как я уже сказал в названии, я действительно ищу только коллекцию add-only (и итерацию).

Существуют ли какие-либо структуры данных, идеально подходящие для этой цели? Или вы можете сами подумать об этом?

4b9b3361

Ответ 1

Существует красивая структура, называемая расширяемым массивом, которая имеет наихудшие значения O (1) и O (n) накладных расходов памяти (то есть она асимптотически сопоставима с динамическим массивом, но имеет O (1) наихудший вставки). Хитрость заключается в том, чтобы использовать подход, который вектор использует - удвоение и копирование, - но сделать копирование ленивым. Например, предположим, что у вас есть массив из четырех элементов, таких как этот:

[1] [2] [3] [4]

Если вы хотите добавить новый номер, скажем, 5, начните с выделения массива, который в два раза больше:

[1] [2] [3] [4]
[ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ]

Затем вы вставляете 5 в новый массив:

[1] [2] [3] [4]
[ ] [ ] [ ] [ ] [5] [ ] [ ] [ ]

Наконец, вытащите 4 из старого массива в новое:

[1] [2] [3] [ ]
[ ] [ ] [ ] [4] [5] [ ] [ ] [ ]

Теперь, когда вы вставляете, добавьте элемент в новый массив и вытащите еще один элемент из старого массива. Например, после добавления 6 мы получили бы

[1] [2] [ ] [ ]
[ ] [ ] [3] [4] [5] [6] [ ] [ ]

После добавления еще двух значений, мы закончим здесь:

[ ] [ ] [ ] [ ]
[1] [2] [3] [4] [5] [6] [7] [8]

Если нам нужно добавить еще один элемент, мы отбросим теперь пустой пустой массив и выделим массив в два раза больше, чем текущий массив (способный удерживать 16 элементов):

[1] [2] [3] [4] [5] [6] [7] [8]
[ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ] [ ]

И повторите этот процесс. Снижая затраты на выделение памяти (обычно это сублинейный размер массива), вы выполняете не более O (1) работу за вставку.

Поиск по-прежнему равен O (1), так как вы просто решаете, какой из двух массивов должен выглядеть, а вставки в середине - O (n) из-за перетасовки.

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

Надеюсь, это поможет!

РЕДАКТИРОВАТЬ. Если вы хотите потратить немного времени на чтение исследовательской работы и попытку реализовать довольно сложную структуру данных, вы можете получить тот же результат (в худшем случае O (1) append) в O (& radic; n) пространственные служебные данные (что, по-видимому, оптимально, кстати), используя идеи в этой статье. Я так и не получил вокруг, чтобы фактически реализовать это, но это, безусловно, стоит того, чтобы читать, если память является супер-дефицитным ресурсом. Интересно, что эта конструкция используется в качестве подпрограммы!

Ответ 3

OK. То, что вы описали, почти то, что std:: deque находится в стандартной библиотеке С++. Разница заключается в том, что массив (обычно) используется для привязки указателей к вспомогательным массивам вместо использования связанного списка.

Ответ 4

Одной из идей было бы создать список из нескольких элементов, например:

struct item
{
    int data[NUM_ITEMS];
    item *next;
}

В этом случае insert примет O(1), и если вы достигнете предела, просто создайте новый блок и добавьте его в конец списка