Здесь много написано об открытых хэш-таблицах, но некоторые основные моменты пропущены.
Практические реализации обычно имеют O (1) поиск и удаление, поскольку они гарантируют, что ведра не будут содержать больше фиксированного количества элементов (коэффициент нагрузки ). Но это означает, что они могут достичь только амортизировано O (1) время для вставки, потому что таблица должна периодически реорганизоваться по мере ее роста.
(Некоторые из них могут выбрать реорганизовать на удалении, а также, чтобы уменьшить таблицу, когда коэффициент нагрузки достигает некоторые нижний порог, выпотрошить это влияют только на пространстве, не асимптотическое время работы.)
Перестройки означает увеличение (или уменьшение) количество ведер и повторное назначение всех элементов в их новые местоположения ковша. Существуют схемы, например. Расширяемое хеширование, чтобы сделать это немного дешевле. Но в целом это означает касание каждого элемента таблицы.
Реорганизация, то, O (n). Как можно вставить O (1), когда любой из них может понести эту стоимость? Секрет - это амортизация и сила власти. Когда таблица выращивается, она должна вырасти на фактор больше одного, два наиболее распространены. Если таблица начинается с 1 ведром и удваивает каждый раз, когда коэффициент нагрузки достигает F, то стоимость N реорганизаций является
F + 2F + 4F + 8F ... (2^(N-1))F = (2^N - 1)F
В этой точке таблица содержит (2^(N-1))F
элементов, число в таблице во время последней реорганизации. То есть мы сделали (2^(N-1))F
вставки, а общая стоимость реорганизации указана справа. Интересная часть является средней стоимости элемента в таблице (или вставьте, сделайте ваш выбор):
(2^N - 1)F 2^N
---------- ~= ------- = 2
(2^(N-1))F 2^(N-1)
Это где амортизируются O (1) приходит.
Еще один момент заключается в том, что для современных процессоров связанные списки не являются отличной идеей для списков ведра. С 8-байтовыми указателями накладные расходы имеют смысл. Что еще более важно, выделенные кучи узлы в одном списке почти никогда не будут смежными в памяти. Перемещение такого списка приводит к снижению производительности кеша, что может замедлить работу на порядок.
Массивы (с целым числом для количества элементов, содержащих данные), скорее всего, будут работать лучше. Если коэффициент нагрузки достаточно мал, просто выделите массив, равный по размеру, коэффициенту загрузки в момент, когда первый элемент будет вставлен в ведро. В противном случае, вырастите эти массивы элементов по факторам так же, как массив ведра! Все будет амортизироваться до O (1).
Чтобы удалить элемент из такого ведра, не удаляйте его. Просто скопируйте последний элемент массива в местоположение удаленной и уменьшите количество элементов. Конечно, это не сработает, если вы разрешите внешние указатели в хэш-ведрах, но это плохая идея.
Сравнение HashSet и LinkedList, вероятно, является плохим выбором. Набор не позволяет дублировать. HashSet вычисляет хэш элементов для определения сходств. –
Не хеширование с привязкой связанного решения для борьбы с столкновением? – driftdrift
Теоретически, да, но столкновений в Python 'set()' не цепочка. Столкновения перезаписываются. –