Различаются ли временные сложности между списком и deque для Bisect / Insort? - PullRequest
0 голосов
/ 26 февраля 2020

Насколько я знаю, list в Python реализован с использованием массива, а deque реализован с использованием двойного связанного списка. В любом случае двоичный поиск определенного значения занимает время O (logn), но если мы вставим в эту позицию, массив займет O (n), а список с двумя связями - O (1).

Итак, можем ли мы использовать комбинацию bisect, insort и deque для реализации всех операций над множествами Dynami c со сложностью по времени, сопоставимой с TreeMap в Java?


Обновление: I проверил это в этом вопросе Leetcode: https://leetcode.com/problems/time-based-key-value-store/submissions/

Вопреки моим ожиданиям, когда я переключаюсь с list на deque, скорость сильно замедляется.

1 Ответ

3 голосов
/ 26 февраля 2020

На ваш заглавный вопрос: Да, они делают.

На ваш гипотетически отсортированный набор вопросов реализации: Нет, вы не можете.

Во-первых, вы ошиблись в реализации deque; это не простой связанный список «элемент на узел», это блок элементов на узел (64 в ссылочном интерпретаторе CPython, хотя это детали реализации). И, кроме головных и хвостовых блоков, внутренние блоки никогда не остаются пустыми, поэтому вставка на полпути через deque не особенно дешевая, она все равно должна перемещать кучу вещей вокруг. Это не O(n), как вставка в середине list, так как она использует некоторые преимущества при вращении для поворота, добавления в одну или другую сторону, а затем поворота назад, но это далеко от вставки в известной точке в связанный список, остающийся O(n) (хотя с большими постоянными делителями благодаря тому, что перетасовка целых блоков обходится дешевле, чем перемещение каждого из отдельных элементов).

Два, каждый поиск в deque равен O(n), не O(1), как list; у него постоянный делитель 64, как указано ранее, и он падает до O(1) около обоих концов deque, но в целом он все равно O(n), что плохо масштабируется для больших deque с. bisect поиск равен O(log n) при условии, что индексация последовательности равна O(1); для deque они будут O(n log n), поскольку они будут выполнять log n O(n) операций индексации. Это соответствует вашим результатам тестирования; bisect + deque значительно хуже.

Java TreeMap не реализовано в терминах бинарного поиска и связанного списка в любом случае; связанные списки не годятся для этого, поскольку в конечном итоге полный двоичный поиск должен проходить туда-сюда настолько, чтобы он выполнял O(n) всей работы, даже если он должен сравниваться только с O(log n) элементами. Древовидной карте нужна какая-то древовидная структура, вы не можете просто подделать ее с помощью связанного списка и хорошего алгоритма.

Встроенные альтернативы включают:

  1. insort нормального list: Конечно, оно O(n) в целом, но дорогая часть (поиск места для вставки) составляет O(log n), и это всего лишь шаг "make room", который O(n), и это действительно дешево O(n) (в основном memcpy). Неприемлемо для действительно огромных list с, но вы будете удивлены, насколько большой list вам понадобится, чтобы накладные расходы были заметны на фоне замедления Python.
  2. Отсроченная буферизованная сортировка: Если поиск нечастый, но вставки встречаются часто, отложите сортировку до тех пор, пока это не потребуется, чтобы минимизировать количество операций сортировки; просто добавьте новые элементы в конец без сортировки и установите флаг «нужна сортировка», и повторите поиск sort перед поиском, когда флаг установлен. Алгоритм TimSort работает очень хорошо, когда входные данные уже в основном отсортированы (гораздо ближе к O(n), чем обычно может выполнить сортировка общего назначения без оптимизации для частично отсортированных), так что это может быть хорошо.
  3. Если вам нужен только самый маленький элемент в любой момент времени, модуль heapq может сделать это с истинными вставками и удалениями O(log n) и получает минимум с O(1) (это всегда индекс 0).
  4. Использовать базу данных sqlite3 (возможно через shelve), соответствующим образом проиндексированную; sqlite3 по умолчанию для индексов используется B-дерево, то есть запросы, упорядоченные с использованием ключей индекса, возвращают результаты в отсортированном порядке «бесплатно».

В противном случае вам придется установить сторонний модуль, который обеспечивает правильный отсортированный тип set.

...