Динамический массив с O (1) удалением любого элемента - PullRequest
5 голосов
/ 29 июня 2009

Этот вопрос о структуре данных, о которой я думал.Это динамический массив, такой как std :: vector <> в C ++, за исключением того, что алгоритм удаления отличается.

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

В этом случае, если какой-либо элемент удаляется, он заменяется последним элементом.Это, конечно, теряет порядок элементов.Но теперь удаление любого элемента имеет постоянное время.

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

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

Итак, есть ли у этой странной / бесполезной структуры имя, и имеет ли она какое-либо применение?Или просто милый маленький мозговой штурм?

Ответы [ 6 ]

5 голосов
/ 29 июня 2009

Эта идея используется в Кнут (Фишер-Йейтс) shuffle . Элемент, выбранный случайным образом, заменяется последним в массиве. Поскольку в любом случае нам нужна случайная перестановка, переупорядочение не имеет значения.

3 голосов
/ 29 июня 2009

Итак, у этой странной / бесполезной структуры есть имя, и есть ли у нее какое-либо применение?

Я использовал нечто подобное при моделировании многопроцессорных систем.

В планировщике процессов, реализованных как конечные автоматы, каждый процесс ожидает внешнего события, активного или завершенного. Планировщик имеет массив указателей на процессы.

Первоначально каждый процесс активен, и у планировщика есть индекс последнего ожидающего и первого завершенного процесса, изначально ноль и длина массива.

V-- waiting
[ A-active, B-active, C-active, D-active ]
                             completed --^
^- run

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

           V-- waiting
[ A-waiting, B-active, C-active, D-active ]
                              completed --^
             ^- run

Если он сообщает, что он завершен, он заменяется процессом до первого завершенного массива.

           V-- waiting
[ A-waiting, D-active, C-active, B-completed ]
                   completed --^
             ^- run

Таким образом, когда планировщик запускается и обрабатывает переход от активного к ожиданию или завершению, массив упорядочивается со всеми ожидающими процессами в начале, всеми активными в середине и завершенными в конце.

                      V-- waiting
[ A-waiting, C-waiting, D-active, B-completed ]
                   completed --^
                        ^- run

После определенного числа итераций или когда больше нет активных процессов, завершенные процессы удаляются из массива и обрабатываются внешние события:

                      V-- waiting
[ A-waiting, C-waiting, D-completed, B-completed ]
          completed --^
                        ^- run == completed so stop

Это похоже на то, что он использует подкачку для удаления элементов из коллекции, но скорее удаляет элементы с обоих концов и оставляет «коллекцию» посередине.

2 голосов
/ 29 июня 2009

Я помню, как использовал этот метод много раз прежде. Но я не знаю его названия.

Простой пример: в компьютерной игре вы перебираете всех «плохих парней», вычисляете их движения и т. Д. С ними может случиться только одно: исчезнуть (их мертвое тело исчезло и теперь прозрачно на 99%). В этот момент вы удаляете его из списка, как и вы, и возобновляете итератор, не увеличивая счетчик итераций.

Нечто подобное происходит в Binary Heap при удалении элемента, однако следующим шагом является поддержание правила кучи - O (log n).

0 голосов
/ 06 января 2010

Я не знаю его названия, но в некоторых случаях оно лучше, чем список.

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

0 голосов
/ 04 августа 2009

Это называется Set .

0 голосов
/ 29 июня 2009

Хм, у этого алгоритма действительно есть время удаления O (1)?

Это будет означать, что

  1. Нахождение удаляемого элемента - O (1)
  2. Нахождение последнего элемента (который заменит удаленный элемент) - O (1)
  3. Нахождение второго по последнему элементу (нового «последнего» элемента) равно O (1)

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...