Чи має python відсортований список?


128

Під якою я маю на увазі структуру з:

  • O (log n) складність для x.push()операцій
  • O (log n) складність для пошуку елемента
  • O (n) складність для обчислення, list(x)яку буде відсортовано

У мене також було пов'язане питання щодо виконання list(...).insert(...)якого зараз тут .


memcpy- це ще операція O (n) . Я не впевнений, як Python точно реалізує списки , але я ставлю на думку, що вони зберігаються у суміжній пам'яті (звичайно, не як пов'язаний список). Якщо це дійсно так, вставка, за допомогою bisectякої ви демонструєте, матиме складність O (n) .
Stephan202

2
На жаль, не вийшла з коробки. Але бібліотека сортованих контейнерів Гранта Дженка є чудовою. stackoverflow.com/a/22616929/284795
Полковник Паніка

Відповіді:


52

Стандартний список Python не відсортований у будь-якій формі. Стандартний модуль heapq можна використовувати для додавання O (log n) до існуючого списку та видалення найменшого з O (log n), але це не відсортований список у вашому визначенні.

Існують різні реалізації збалансованих дерев для Python, які відповідають вашим вимогам, наприклад, rbtree , RBTree або pyavl .


1
+1 для rbtree, він працює дуже добре (але містить рідний код; не чистий python, можливо, не так просто розгортати)
Буде

12
sortedcontainers - чисто-Python та швидкий-як-C (як rbtree) зі порівнянням продуктивності.
GrantJ

"не є відсортованим списком у вашому визначенні." Як так?
Полковник Паніка

4
heapq дозволяє знайти лише найменший елемент; ОП просила структуру, яка може знайти будь-який елемент у O (log n), який купи не є.
Мартін проти Левіса

70

Чи є певна причина для ваших великих вимог? Або ви просто хочете, щоб це було швидко? Модуль сортованих контейнерів є чисто-Python та швидким (як у швидких реалізаціях на зразок C, таких як blist та rbtree).

У порівнянні продуктивності показує , що контрольні показники швидше або на одному рівні з відсортованого списку типу Blist в. Зауважте також, що rbtree, RBTree та PyAVL надають сортовані типи та набори типів, але не мають сортованого типу списку.

Якщо продуктивність є вимогою, завжди пам’ятайте, що вона орієнтується. Модуль, який обґрунтовує претензію на швидкість з нотацією Big-O, повинен бути підозрюваним, поки він також не покаже порівняльні порівняння.

Відмова: Я є автором модуля сортування контейнерів Python.


Установка:

pip install sortedcontainers

Використання:

>>> from sortedcontainers import SortedList
>>> l = SortedList()
>>> l.update([0, 4, 1, 3, 2])
>>> l.index(3)
3
>>> l.add(5)
>>> l[-1]
5

4
Дійсно, я порівняв сортовані контейнери з бісектними: 0.0845024989976для SortedList.add () і 0.596589182518для bisect.insort (), таким чином різниця у швидкості 7x! І я очікую, що розрив у швидкості збільшиться із довжиною списку, оскільки сортування вкладених контейнерів працює в O (log n), а bisect.insort () в O (n).
габоровий

1
@gaborous тому, що bisect все ще використовує список, тому вставка залишаєтьсяO(n)
njzk2

34

Хоча я ще ніколи не перевіряв "великі" швидкості основних операцій зі списком Python, bisectстандартний модуль, мабуть, також варто згадати в цьому контексті:

import bisect
L = [0, 100]

bisect.insort(L, 50)
bisect.insort(L, 20)
bisect.insort(L, 21)

print L
## [0, 20, 21, 50, 100]

i = bisect.bisect(L, 20)
print L[i-1], L[i]
## 20, 21

PS. Ах, вибачте, bisectзгадується у посиланому запитанні. Все-таки я думаю, це не буде великою шкодою, якщо ця інформація буде тут)

PPS. А списки CPython - це фактично масиви (не скажімо, списки пропускань тощо). Ну, мабуть, вони повинні бути чимось простими, але, як на мене, назва трохи вводить в оману.


Тож, якщо я не помиляюся, швидкість бісект / списку, ймовірно, буде:

  • для push (): O (n) у гіршому випадку;
  • для пошуку: якщо ми вважаємо швидкість індексації масиву O (1), пошук повинен бути операцією O (log (n));
  • для створення списку: O (n) повинна бути швидкістю копіювання списку, інакше це O (1) для того ж списку)

Оновити Після обговорення в коментарях дозвольте мені зв’язати ось такі питання ТА: Як реалізується список Python та яка складність виконання списку функцій python


push () повинен бути в O (log n), оскільки список вже відсортований.
estani

1
можливо, я повинен був сказати "для вставки оп" . так чи інакше, це було близько року тому, і тепер я легко змішую речі або щось пропускаю
ジ ョ ー ジ

Ви завжди можете вставити значення у відсортований список у O (log n), див. Двійковий пошук. push () визначається як операція вставки.
естані

2
Правда. Але хоча пошук місця вставки дійсно займе O (log n) ops, фактична вставка (тобто додавання елемента до структури даних), ймовірно, залежить від цієї структури (подумайте, як вставити елемент у відсортований масив). Оскільки списки Python насправді є масивами , це може зайняти O (n). Зважаючи на обмеження розміру коментарів, я зв'язатиму два відповідні питання ЗУ з тексту відповіді (див. Вище).
ジ ョ ー ジ

Хороший аргумент. Я не знав списку, де обробляються як масиви в Python.
estani

7
import bisect

class sortedlist(list):
    '''just a list but with an insort (insert into sorted position)'''
    def insort(self, x):
        bisect.insort(self, x)

мається на увазі вставка () у bisect.insort () - це O (n)
j314erre

6

Хоча це ще не забезпечує функцію пошуку, користувач heapqможе відповідати вашим потребам. Він реалізує групу купи, використовуючи звичайний список. Вам доведеться написати власний ефективний тест на членство, який використовує внутрішню структуру черги (це можна зробити в O (журнал n) , я б сказав ...). Є один недолік: вилучення відсортованого списку має складність O (n log n) .


Приємно, але важко розділити.
ілля н.

3
Як може бути тест членства на O (log n) у купі? Якщо ви шукаєте значення x, ви можете зупинити погляд на гілці, якщо ви знайдете щось більше, ніж x, але для випадкової величини x це 50%, ймовірно, на листі, і ви, ймовірно, не можете багато обрізати.
ринки

1

Я б використовував biscectабо sortedcontainersмодулі. Я насправді не досвідчений, але думаю, що heapqмодуль працює. Він містить aHeap Queue


0

Реалізувати власний список сортування на Python може не важко. Нижче наведено доказ концепції:

import bisect

class sortlist:
    def __init__(self, list):
        self.list = list
        self.sort()
    def sort(self):
        l = []
        for i in range(len(self.list)):
            bisect.insort(l, self.list[i])
        self.list = l
        self.len = i
    def insert(self, value):
        bisect.insort(self.list, value)
        self.len += 1
    def show(self):
        print self.list
    def search(self,value):
        left = bisect.bisect_left(self.list, value)
        if abs(self.list[min([left,self.len-1])] - value) >= abs(self.list[left-1] - value):
            return self.list[left-1]
        else:
            return self.list[left]

list = [101, 3, 10, 14, 23, 86, 44, 45, 45, 50, 66, 95, 17, 77, 79, 84, 85, 91, 73]
slist = sortlist(list)
slist.show()
slist.insert(99)
slist.show()
print slist.search(100000000)
print slist.search(0)
print slist.search(56.7)

========= Результати =============

[3, 10, 14, 17, 23, 44, 45, 45, 50, 66, 73, 77, 79, 84, 85, 86, 91, 95, 101]

[3, 10, 14, 17, 23, 44, 45, 45, 50, 66, 73, 77, 79, 84, 85, 86, 91, 95, 99, 101]

101

3

50

Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.