Apakah python memiliki daftar yang disortir?


128

Maksud saya struktur dengan:

  • O (log n) kompleksitas untuk x.push()operasi
  • O (log n) kompleksitas untuk menemukan elemen
  • O (n) kompleksitas untuk menghitung list(x)yang akan diurutkan

Saya juga punya pertanyaan terkait tentang kinerja list(...).insert(...)yang sekarang ada di sini .


memcpymasih merupakan operasi O (n) . Saya tidak yakin bagaimana Python mengimplementasikan daftar persis , tetapi taruhan saya adalah bahwa mereka disimpan dalam memori yang bersebelahan (tentu bukan sebagai daftar tertaut). Jika memang demikian, penyisipan bisectyang Anda gunakan akan memiliki kompleksitas O (n) .
Stephan202

2
Sayangnya tidak keluar kotak. Tapi perpustakaan penerima Grant Jenk diurutkan sangat baik. stackoverflow.com/a/22616929/284795
Kolonel Panic

Jawaban:


52

Daftar Python standar tidak diurutkan dalam bentuk apa pun. Modul heapq standar dapat digunakan untuk menambahkan dalam O (log n) ke daftar yang ada dan menghapus yang terkecil di O (log n), tetapi bukan daftar yang diurutkan dalam definisi Anda.

Ada berbagai implementasi pohon seimbang untuk Python yang memenuhi persyaratan Anda, misalnya rbtree , RBTree , atau pyavl .


1
+1 untuk rbtree, ini berfungsi dengan sangat baik (tetapi mengandung kode asli; bukan python murni, mungkin tidak semudah itu untuk digunakan)
Will

12
kontainer diurutkan adalah murni-Python dan cepat-as-C (seperti rbtree) dengan perbandingan kinerja.
GrantJ

"Bukan daftar yang diurutkan dalam definisi Anda." Bagaimana?
Kolonel Panic

4
heapq hanya memungkinkan untuk menemukan elemen terkecil; OP meminta struktur yang dapat menemukan elemen apa pun di O (log n), yang tumpukannya tidak.
Martin v. Löwis

70

Apakah ada alasan khusus untuk persyaratan O besar Anda? Atau Anda hanya ingin cepat? The sortedcontainers modul murni Python dan cepat (seperti dalam implementasi cepat-sebagai-C seperti blist dan rbtree).

The perbandingan kinerja menunjukkan itu benchmark lebih cepat atau setara dengan blist ini diurutkan daftar jenis. Perhatikan juga bahwa rbtree, RBTree, dan PyAVL memberikan dict dict dan atur tipe tetapi tidak memiliki tipe daftar diurutkan.

Jika kinerja merupakan persyaratan, selalu ingat untuk melakukan tolok ukur. Modul yang mendukung klaim cepat dengan notasi Big-O harus dicurigai sampai ia juga menunjukkan perbandingan benchmark.

Penafian: Saya penulis modul Python diurutkan kontainer.


Instalasi:

pip install sortedcontainers

Pemakaian:

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

4
Memang saya membandingkan sortcontainers terhadap bisect: 0.0845024989976untuk SortedList.add () vs 0.596589182518untuk bisect.insort (), dengan demikian perbedaan 7x dalam kecepatan! Dan saya berharap kesenjangan kecepatan meningkat dengan panjang daftar karena jenis penyisipan sortcontainers bekerja di O (log n) sementara bisect.insort () di O (n).
Gaborous

1
@ rumit karena dua bagian masih menggunakan daftar, jadi penyisipan tetapO(n)
njzk2

34

Meskipun saya masih belum pernah memeriksa kecepatan "O besar" dari operasi daftar Python dasar, bisectmodul standar mungkin juga layak disebutkan dalam konteks ini:

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. Ah, maaf, bisectdisebutkan dalam pertanyaan yang dirujuk. Tetap saja, saya pikir tidak akan banyak bahaya jika informasi ini ada di sini)

PPS. Dan daftar CPython sebenarnya adalah array (bukan, katakanlah, daftar hitam atau lain-lain). Yah, saya kira mereka harus sesuatu yang sederhana, tetapi bagi saya, namanya agak menyesatkan.


Jadi, jika saya tidak salah, kecepatan dua bagian / daftar mungkin akan:

  • untuk dorongan (): O (n) untuk kasus terburuk;
  • untuk pencarian: jika kami menganggap kecepatan pengindeksan array menjadi O (1), pencarian harus menjadi operasi O (log (n));
  • untuk pembuatan daftar: O (n) harus menjadi kecepatan penyalinan daftar, jika tidak maka O (1) untuk daftar yang sama)

Pembaruan. Setelah diskusi dalam komentar, izinkan saya menautkan di sini pertanyaan SO ini: Bagaimana Daftar Python Diimplementasikan dan Apa kompleksitas runtime dari fungsi daftar python


push () harus dalam O (log n) karena daftar sudah diurutkan.
estani

1
mungkin saya seharusnya mengatakan "untuk memasukkan op" . lagi pula, itu sekitar setahun yang lalu jadi sekarang saya dapat dengan mudah mencampuradukkan atau melewatkan sesuatu
ジ ョ ー ジ

Anda selalu dapat memasukkan nilai ke daftar yang diurutkan dalam O (log n), lihat pencarian biner. push () didefinisikan sebagai operasi sisipan.
estani

2
Benar. Tetapi ketika menemukan lokasi penyisipan memang akan mengambil O (log n) ops, memasukkan sebenarnya (yaitu menambahkan elemen ke struktur data) mungkin tergantung pada struktur itu (pikirkan memasukkan elemen dalam array yang diurutkan). Dan karena daftar Python sebenarnya adalah array , ini mungkin membutuhkan O (n). Karena batas ukuran untuk komentar, saya akan menautkan dua pertanyaan SO terkait dari teks jawaban (lihat di atas).
ジ ョ ー ジ

Argumen yang bagus. Saya tidak tahu daftar mana yang ditangani sebagai array dengan 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)

insert tersirat () dalam bisect.insort () adalah O (n)
j314erre

6

Meskipun tidak (belum) menyediakan fungsi pencarian khusus, heapqmodul mungkin sesuai dengan kebutuhan Anda. Ini mengimplementasikan antrian tumpukan menggunakan daftar biasa. Anda harus menulis tes keanggotaan efisien Anda sendiri yang menggunakan struktur internal antrian (yang dapat dilakukan di O (log n) , menurut saya ...). Ada satu kelemahan: mengekstraksi daftar yang diurutkan memiliki kompleksitas O (n log n) .


Bagus tapi sulit untuk membagi dua.
ilya n.

3
Bagaimana bisa ada tes keanggotaan O (log n) di heap? Jika Anda mencari nilai x, Anda bisa berhenti melihat ke bawah cabang jika Anda menemukan sesuatu yang lebih besar dari x, tetapi untuk nilai acak x itu kemungkinan 50% berada di daun, dan Anda mungkin tidak bisa memangkas banyak.
memasarkan

1

Saya akan menggunakan modul biscectatau sortedcontainers. Saya tidak benar-benar berpengalaman, tapi saya pikir heapqmodul ini berfungsi. Berisi aHeap Queue


0

Mungkin tidak sulit untuk mengimplementasikan daftar sortir Anda sendiri di Python. Di bawah ini adalah bukti konsep:

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)

========= Hasil ============

[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

Dengan menggunakan situs kami, Anda mengakui telah membaca dan memahami Kebijakan Cookie dan Kebijakan Privasi kami.
Licensed under cc by-sa 3.0 with attribution required.