Con trăn có một danh sách được sắp xếp?


128

Ý tôi là một cấu trúc với:

  • Độ phức tạp O (log n) cho các x.push()hoạt động
  • Độ phức tạp O (log n) để tìm một phần tử
  • Độ phức tạp O (n) để tính toán list(x)sẽ được sắp xếp

Tôi cũng đã có một câu hỏi liên quan về hiệu suất list(...).insert(...)hiện đang ở đây .


memcpyvẫn là một hoạt động O (n) . Tôi không chắc chắn làm thế nào Python thực hiện danh sách chính xác , nhưng đặt cược của tôi sẽ là chúng được lưu trữ trong bộ nhớ liền kề (chắc chắn không phải là một danh sách được liên kết). Nếu đó thực sự là như vậy, việc chèn sử dụng bisectmà bạn chứng minh sẽ có độ phức tạp O (n) .
Stephan202

2
Đáng buồn không ra khỏi hộp. Nhưng thư viện sortcontainers của Grant Jenk là tuyệt vời. stackoverflow.com/a/22616929/284795
Đại tá Panic

Câu trả lời:


52

Danh sách Python chuẩn không được sắp xếp dưới mọi hình thức. Mô-đun heapq tiêu chuẩn có thể được sử dụng để nối thêm O (log n) vào danh sách hiện có và xóa cái nhỏ nhất trong O (log n), nhưng không phải là danh sách được sắp xếp theo định nghĩa của bạn.

Có nhiều cách triển khai cây cân bằng khác nhau cho Python đáp ứng yêu cầu của bạn, ví dụ: rbtree , RBTree hoặc pyavl .


1
+1 cho rbtree, nó hoạt động rất tốt (nhưng chứa mã gốc; không phải python thuần, có thể không dễ triển khai)
Will

12
sortcontainers là thuần Python và nhanh như C (như rbtree) với so sánh hiệu suất.
GrantJ

"không phải là một danh sách được sắp xếp trong định nghĩa của bạn." Làm sao vậy
Đại tá Panic

4
heapq chỉ cho phép tìm phần tử nhỏ nhất; OP đã yêu cầu một cấu trúc có thể tìm thấy bất kỳ phần tử nào trong O (log n), mà heaps thì không.
Martin v. Löwis

70

Có một lý do cụ thể cho các yêu cầu big-O của bạn không? Hay bạn chỉ muốn nó được nhanh? Các sortedcontainers mô-đun là thuần Python và nhanh chóng (như trong triển khai nhanh-as-C như blist và rbtree).

Các hiệu suất so sánh chương trình nó benchmarks nhanh hơn hoặc ngang bằng với loại danh sách được sắp xếp blist của. Cũng lưu ý rằng rbtree, RBTree và PyAVL cung cấp các loại chính tả và tập hợp được sắp xếp nhưng không có loại danh sách được sắp xếp.

Nếu hiệu suất là một yêu cầu, luôn luôn nhớ điểm chuẩn. Một mô-đun chứng minh tuyên bố là nhanh với ký hiệu Big-O nên bị nghi ngờ cho đến khi nó cũng hiển thị so sánh điểm chuẩn.

Tuyên bố miễn trừ trách nhiệm: Tôi là tác giả của mô-đun trình sắp xếp Python.


Cài đặt:

pip install sortedcontainers

Sử dụng:

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

4
Thật vậy, tôi đã so sánh các trình điều khiển được sắp xếp với bisect: 0.0845024989976for SortedList.add () so 0.596589182518với bisect.insort (), do đó chênh lệch 7 lần về tốc độ! Và tôi hy vọng khoảng cách tốc độ sẽ tăng theo chiều dài danh sách vì sắp xếp bộ chèn sắp xếp hoạt động trong O (log n) trong khi bisect.insort () trong O (n).
gabious

1
@gabious vì bisect vẫn sử dụng một danh sách, vì vậy phần chèn vẫn cònO(n)
njzk2

34

Mặc dù tôi vẫn chưa bao giờ kiểm tra tốc độ "O lớn" của các hoạt động danh sách Python cơ bản, bisectmô-đun tiêu chuẩn có lẽ cũng đáng được đề cập trong ngữ cảnh này:

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

Tái bút Ah, xin lỗi, bisectđược đề cập trong câu hỏi tham khảo. Tuy nhiên, tôi nghĩ sẽ không có hại gì nếu thông tin này sẽ ở đây)

PPS. Và danh sách CPython thực sự là mảng (không, giả sử, skiplists hoặc vv). Chà, tôi đoán chúng phải là một cái gì đó đơn giản, nhưng đối với tôi, cái tên hơi sai lệch.


Vì vậy, nếu tôi không nhầm, tốc độ chia đôi / danh sách có thể là:

  • cho một đẩy (): O (n) cho trường hợp xấu nhất;
  • đối với tìm kiếm: nếu chúng ta coi tốc độ lập chỉ mục mảng là O (1), thì tìm kiếm phải là thao tác O (log (n));
  • đối với việc tạo danh sách: O (n) phải là tốc độ sao chép danh sách, nếu không thì đó là O (1) cho cùng một danh sách)

Cập nhật. Sau một cuộc thảo luận trong các bình luận, hãy để tôi liên kết ở đây những câu hỏi SO này: Danh sách của Python được triển khai như thế nàođộ phức tạp thời gian chạy của các hàm danh sách python là gì


push () nên ở O (log n) vì danh sách đã được sắp xếp.
estani

1
có lẽ tôi nên nói "cho một op op" . dù sao, đó là khoảng một năm trước vì vậy bây giờ tôi có thể dễ dàng trộn lẫn mọi thứ hoặc bỏ lỡ một cái gì đó
ジ ョ ー

Bạn luôn có thể chèn một giá trị vào danh sách được sắp xếp trong O (log n), xem tìm kiếm nhị phân. push () được định nghĩa là một hoạt động chèn.
estani

2
Thật. Nhưng trong khi tìm vị trí chèn thực sự sẽ mất O (log n) ops, thì chèn thực tế (tức là thêm phần tử vào cấu trúc dữ liệu) có thể phụ thuộc vào cấu trúc đó (nghĩ rằng chèn một phần tử trong một mảng được sắp xếp). Và vì danh sách Python thực sự là mảng , nên điều này có thể mất O (n). Do giới hạn kích thước cho các bình luận, tôi sẽ liên kết hai câu hỏi SO liên quan từ văn bản của câu trả lời (xem ở trên).
ジ ョ ー

Lý lẽ tốt. Tôi không biết danh sách nơi xử lý như mảng trong 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)

hàm ẩn () trong bisect.insort () là O (n)
j314erre

6

Mặc dù nó không (chưa) cung cấp chức năng tìm kiếm tùy chỉnh, heapqmô-đun có thể phù hợp với nhu cầu của bạn. Nó thực hiện một hàng đợi sử dụng một danh sách thông thường. Bạn sẽ phải viết bài kiểm tra thành viên hiệu quả của riêng bạn, sử dụng cấu trúc bên trong của hàng đợi (có thể được thực hiện trong O (log n) , tôi muốn nói ...). Có một nhược điểm: trích xuất danh sách đã sắp xếp có độ phức tạp O (n log n) .


Nó đẹp nhưng khó chia đôi.
ilya n.

3
Làm thế nào có thể có một bài kiểm tra thành viên O (log n) trong một đống? Nếu bạn đang tìm kiếm giá trị x, bạn có thể ngừng nhìn xuống một nhánh nếu bạn tìm thấy giá trị nào đó lớn hơn x, nhưng với giá trị ngẫu nhiên của x, nó có khả năng nằm ở một chiếc lá và bạn có thể không cắt tỉa nhiều.
thị trường

1

Tôi sẽ sử dụng biscecthoặc sortedcontainersmô-đun. Tôi không thực sự có kinh nghiệm, nhưng tôi nghĩ rằng heapqmô-đun hoạt động. Nó chứa mộtHeap Queue


0

Có thể không khó để thực hiện danh sách sắp xếp của riêng bạn trên Python. Dưới đây là một bằng chứng về khái niệm:

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)

========= Kết quả ============

[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

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.