2009-07-10 3 views
101

я имею в виду структуру с:Есть ли у python отсортированный список?

  • O (журнал N) сложности для x.push() операций
  • O (журнал N) сложность найти элемент
  • O (N) сложность вычисления list(x), который будет отсортирован

Я также имел связанный с этим вопрос о выполнении list(...).insert(...) который сейчас here.

+0

'memcpy' по-прежнему работает * O (n) *. Я не уверен, как Python реализует списки * точно *, но моя ставка будет заключаться в том, что они хранятся в непрерывной памяти (конечно, не как связанный список). Если это действительно так, вставка с использованием 'bisect', которую вы продемонстрируете, будет иметь сложность * O (n) *. – Stephan202

+2

... и затем этот пример не был;) – Stephan202

+0

@ stephan202: Извините, я думал, что он заслуживает самого себя, как совершенно отдельный вопрос! –

ответ

43

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

Существуют различные реализации сбалансированных деревьев для Python, которые отвечают вашим требованиям, например. rbtree, RBTree, или pyavl.

+1

+1 для rbtree, он работает очень хорошо (но содержит собственный код, а не чистый python, не так просто для развертывания) – Will

+11

[отсортированные контейнеры] (http: // www .grantjenks.com/docs/sortedcontainers /) - это чистый-Python и fast-as-C (например, rbtree) с сопоставлением производительности. – GrantJ

+0

"не является отсортированным списком в вашем определении." Как так? –

6

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

+0

Это приятно, но трудно делить пополам. –

+3

Как может быть тест членства O (log n) в куче? Если вы ищете значение x, вы можете перестать смотреть вниз по ветке, если вы найдете что-то большее, чем x, но для случайного значения x оно будет на 50% вероятнее всего на листе, и вы, вероятно, не сможете много обрезать. – markets

27

Хотя я никогда еще не проверяли на «большой O» скорости базовых операций списка 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 lists are actually arrays (не скажем, скиписты и т. Д.). Ну, я думаю, они должны быть чем-то простым, но что касается меня, это имя немного вводит в заблуждение.


Так что, если я не ошибаюсь, скорость Bisect/список, вероятно, будет:

  • толчка(): O (п) для наихудшего случая;
  • для поиска: если мы считаем, что скорость индексации массива равна O (1), поиск должен быть операцией O (log (n));
  • для создания списка: O (N) должен быть скорость списка копирования, в противном случае это O (1) для того же списка)

Upd. После обсуждения в комментариях, дайте мне ссылку здесь эти SO вопросы: How is Python's List Implemented и What is the runtime complexity of python list functions

+0

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

+1

Возможно, я должен был сказать ["для вставки op"] (http://docs.python.org/library/bisect.html#bisect.insort_left). во всяком случае, это было около года назад, так что теперь я могу легко перемешать вещи или пропустить что-то. –

+0

Вы всегда можете вставить значение в отсортированный список в O (log n), см. двоичный поиск. push() определяется как операция вставки. – estani

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]

43

Есть ли особая причина для ваших требований к большому O? Или вы просто хотите, чтобы это было быстро? Модуль sortedcontainers является чистым-Python и быстрым (как в реализациях fast-as-C, таких как blist и rbtree).

performance comparison показывает тесты быстрее или наравне с сортированным списком типа blist. Также обратите внимание, что rbtree, RBTree и PyAVL предоставляют отсортированные типы dict и set, но не имеют отсортированного типа списка.

Если производительность является обязательным требованием, всегда помните о контроле. Модуль, который обосновывает требование быть быстрым с нотами 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 
+2

Действительно, я сравнил отсортированные контейнеры против bisect: '0.0845024989976' для SortedList.add() против' 0,596589182518' для bisect.insort(), таким образом, разница в 7x по скорости! И я ожидаю, что разрыв скорости увеличится с длиной списка, так как сортировка сортировки контейнеров в O (log n) в то время как bisect.insort() в O (n). – gaborous

+1

@gaborous, потому что bisect все еще использует список, поэтому вставка остается 'O (n)' – njzk2

2
import bisect 

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

Я бы использовать biscect или sortedcontainers модули. Я не очень опытный, но я думаю, что модуль heapq работает. Он содержит Heap Queue

Смежные вопросы