Несоответствия в Big-O удаления из ArrayList и хеш-таблицы?

Я просматриваю этот веб-сайт, на котором перечислены сложности Big O для различных операций. Для динамических массивов сложность удаления составляет O(n), а для хеш-таблиц — O(1).

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

Но в хеш-таблицах с линейным зондированием удаление — это то же самое, вы просто запускаете свое значение через хэш-функцию, переходите к динамическому массиву, содержащему ваши данные, и удаляете хранящееся в нем значение.

Так почему же хэш-таблицы получают кредит O(1), а динамические массивы — O(n)?


person user1956609    schedule 11.12.2013    source источник
comment
Почему они должны быть «последовательными»? Рассматривали ли вы возможность того, что ваши предположения неверны?   -  person user207421    schedule 11.12.2013
comment
Я предлагаю вам удалить тег java и упоминания ArrayList и заменить его на не зависит от языка, если вы действительно хотите, но просто данные -structures также подходит, если только этот вопрос не касается конкретно реализации хеш-таблицы API Java (обратите внимание, что здесь не используется линейное зондирование, используется отдельная цепочка, как Стивен указал).   -  person Bernhard Barker    schedule 11.12.2013


Ответы (4)


Это объясняется здесь. Ключевым моментом является то, что количество значений в динамическом массиве поддерживается на постоянном уровне.

Изменить: как отметил Дюклинг, мой ответ объясняет, почему хеш-таблица с отдельной цепочкой имеет сложность удаления O (1). Я должен добавить, что на веб-сайте, на который вы смотрели, хеш-таблицы имеют сложность удаления O (1), потому что они анализируют хеш-таблицу с отдельной цепочкой, а не с линейным зондированием.

person Jelle Fresen    schedule 11.12.2013
comment
Вопрос касается линейного зондирования, а не отдельной цепочки. Ну, я предполагаю, что вы говорите об отдельной цепочке, иначе то, что вы сказали, не имеет смысла. - person Bernhard Barker; 11.12.2013

Смысл хеш-таблиц в том, что они близки к лучшему случаю, где лучший случай означает одну запись в корзине. Очевидно, вы без труда согласитесь с тем, что удаление единственной записи из корзины занимает O(1) времени.

person Marko Topolnik    schedule 11.12.2013
comment
Трудно сказать, говорите ли вы о линейном зондировании или отдельной цепочке (кажется, это отдельная цепочка, но, с другой стороны, имеет смысл, если это также и линейное зондирование). Вопрос касается линейного зондирования. Я думал, что просто укажу на это. - person Bernhard Barker; 11.12.2013
comment
Я говорю об обоих. Поскольку линейное зондирование не имеет ничего общего с HashMap, я предположил, что OP не заботится о подробном различии. - person Marko Topolnik; 11.12.2013

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

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

Когда это происходит, нам нужно только удалить какое-то значение и сместить либо отсутствие значений, либо небольшое (по существу постоянное) количество значений.

person Bernhard Barker    schedule 11.12.2013

Когда вы говорите о сложности алгоритма, на самом деле вам нужно обсуждать конкретную реализацию.

  • Не существует класса Java, который называется «Хеш-таблица» (очевидно!) или «Хэш-таблица».

  • Существуют классы Java с именами HashMap и Hashtable, и в них действительно есть удаление O(1).

Но они не работают так, как вы думаете (все?) работают хеш-таблицы. В частности, HashMap и Hashtable организованы как массив указателей на «цепочки».

Это означает, что удаление состоит из поиска соответствующей цепочки, а затем обхода цепочки, чтобы найти запись, которую нужно удалить. Первый шаг — постоянное время (включая время вычисления хеш-кода. Второй шаг пропорционален длине хэш-цепочек. Но если предположить, что хеш-функция хорошая, средняя длина хэш-цепочки — небольшая константа. Следовательно, общее время удаления в среднем составляет O(1).


Причина того, что хеш-цепочки в среднем короткие, заключается в том, что классы HashMap и Hashtable автоматически изменяют размер основного хэш-массива, когда "коэффициент загрузки" (отношение размера массива к количеству записей) превышает заданное значение. Предполагая, что хеш-функция распределяет (фактические) ключи довольно равномерно, вы обнаружите, что цепочки имеют примерно одинаковую длину. Предполагая, что размер массива пропорционален общему количеству записей, фактический коэффициент загрузки будет равен средней длине хеш-цепочки.

Это рассуждение не работает, если хеш-функция не распределяет ключи равномерно. Это приводит к ситуации, когда возникает много коллизий хэшей. Действительно, наихудший случай — это когда все ключи имеют одинаковое хеш-значение и все они оказываются в одной хеш-цепочке со всеми N записями. В этом случае удаление включает в себя поиск цепочки с N элементами... и это делает ее O(N).


Оказывается, те же рассуждения можно применить и к другим формам хеш-таблиц, в том числе к тем, в которых записи хранятся в самом хэш-массиве, а коллизии обрабатываются путем повторного хеширования. (Еще раз, «хитрость» заключается в расширении хэш-таблицы, когда коэффициент загрузки становится слишком высоким.)

person Stephen C    schedule 11.12.2013
comment
Классы Java также не могут содержать пробелы, поэтому я думаю, что OP на самом деле говорил не о классе Java, а просто о хеш-таблицах в целом с линейным зондированием (о чем было явно упомянуто), а не просто опечатка, и не зная, что класс Java не использует линейное зондирование. - person Bernhard Barker; 11.12.2013