python มีรายการเรียงลำดับหรือไม่?


128

โดยฉันหมายถึงโครงสร้างที่มี:

  • O (log n) ความซับซ้อนสำหรับx.push()การดำเนินการ
  • O (log n) ความซับซ้อนในการค้นหาองค์ประกอบ
  • O (n) ความซับซ้อนในการคำนวณlist(x)ซึ่งจะเรียงลำดับ

ฉันยังมีคำถามที่เกี่ยวข้องเกี่ยวกับประสิทธิภาพของlist(...).insert(...)ซึ่งขณะนี้ที่นี่


memcpyก็ยังคงเป็นO (n)การดำเนินงาน ผมไม่แน่ใจว่างูหลามดำเนินรายการว่าแต่เดิมพันของฉันจะเป็นว่าพวกเขาจะถูกเก็บไว้ในหน่วยความจำที่อยู่ติดกัน (แน่นอนไม่ได้เป็นรายการที่เชื่อมโยง) ถ้าเป็นจริงเช่นนั้นแทรกการใช้bisectที่คุณแสดงให้เห็นถึงจะมีความซับซ้อนO (n)
Stephan202

2
น่าเศร้าที่ไม่ออกนอกกรอบ แต่แกรนท์ Jenk ของsortedcontainersห้องสมุดเป็นเลิศ stackoverflow.com/a/22616929/284795
Colonel Panic

คำตอบ:


52

รายการ Python มาตรฐานไม่ได้จัดเรียงในรูปแบบใด ๆ โมดูลheapqมาตรฐานสามารถใช้เพื่อผนวก O (log n) เข้ากับรายการที่มีอยู่และลบรายการที่เล็กที่สุดใน O (log n) ได้ แต่ไม่ใช่รายการที่เรียงลำดับในคำจำกัดความของคุณ

มีการใช้งานต่างๆของต้นไม้สมดุลสำหรับงูหลามที่ตอบสนองความต้องการของคุณเช่นrbtree , RBTreeหรือpyavl


1
+1 สำหรับ rbtree มันใช้งานได้ดีมาก (แต่มีโค้ดเนทีฟไม่ใช่ python บริสุทธิ์บางทีอาจจะใช้งานได้ง่าย)
จะ

12
sortedcontainersเป็น pure-Python และ fast-as-C (เช่น rbtree) พร้อมการเปรียบเทียบประสิทธิภาพ
GrantJ

"ไม่ใช่รายการที่จัดเรียงในคำจำกัดความของคุณ" ยังไง?
พันเอก Panic

4
heapq อนุญาตให้ค้นหาองค์ประกอบที่เล็กที่สุดเท่านั้น OP กำลังขอโครงสร้างที่สามารถค้นหาองค์ประกอบใด ๆ ใน O (log n) ซึ่งฮีปไม่ใช่
Martin v. Löwis

70

มีเหตุผลเฉพาะสำหรับข้อกำหนดขนาดใหญ่ของคุณหรือไม่? หรือคุณแค่อยากให้มันเร็ว? sortedcontainersโมดูลบริสุทธิ์หลามและรวดเร็ว (ในขณะที่การใช้งานอย่างรวดเร็วเป็น-C เช่น BLIST และ rbtree)

การเปรียบเทียบประสิทธิภาพของการแสดงมันเร็วขึ้นหรือมาตรฐานในหุ้นที่มีรายการประเภท BLIST เรียง โปรดทราบว่า rbtree, RBTree และ PyAVL มีการจัดเรียง dict และประเภทการตั้งค่า แต่ไม่มีประเภทรายการที่เรียงลำดับ

หากประสิทธิภาพเป็นสิ่งจำเป็นอย่าลืมเปรียบเทียบเสมอ โมดูลที่ยืนยันการอ้างสิทธิ์ว่ารวดเร็วด้วยสัญลักษณ์ Big-O ควรต้องสงสัยจนกว่าจะแสดงการเปรียบเทียบมาตรฐานด้วย

ข้อจำกัดความรับผิดชอบ:ฉันเป็นผู้เขียนโมดูล Python sortedcontainers


การติดตั้ง:

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
อันที่จริงฉันเปรียบเทียบคอนเทนเนอร์ที่เรียงลำดับกับ bisect: 0.0845024989976สำหรับ SortedList.add () เทียบ0.596589182518กับ bisect.insort () ดังนั้นความแตกต่างของความเร็ว 7 เท่า! และฉันคาดว่าช่องว่างความเร็วจะเพิ่มขึ้นตามความยาวของรายการเนื่องจากการเรียงลำดับการแทรกคอนเทนเนอร์ที่เรียงลำดับทำงานใน O (log n) ในขณะที่ bisect.insort () ใน O (n)
พูดไม่ออก

1
@gaborous เพราะ bisect ยังคงใช้รายการดังนั้นการแทรกยังคงอยู่O(n)
njzk2

34

แม้ว่าฉันจะยังไม่เคยตรวจสอบความเร็ว "big 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 เป็นอาร์เรย์จริงๆ (ไม่ใช่เช่น skiplists หรืออื่น ๆ ) ฉันเดาว่าพวกเขาต้องเป็นอะไรที่เรียบง่าย แต่สำหรับฉันแล้วชื่อนั้นทำให้เข้าใจผิดเล็กน้อย


ดังนั้นถ้าฉันจำไม่ผิดความเร็วทวิภาค / รายการอาจเป็น:

  • สำหรับ push (): O (n) สำหรับกรณีที่เลวร้ายที่สุด;
  • สำหรับการค้นหา: ถ้าเราพิจารณาความเร็วของการสร้างดัชนีอาร์เรย์เป็น O (1) การค้นหาควรเป็นการดำเนินการ O (log (n))
  • สำหรับการสร้างรายการ: O (n) ควรเป็นความเร็วในการคัดลอกรายการมิฉะนั้นจะเป็น O (1) สำหรับรายการเดียวกัน)

UPD หลังจากการอภิปรายในความคิดเห็นให้ฉันเชื่อมโยงคำถาม SO เหล่านี้ที่นี่: รายการของ Python ถูกนำไปใช้อย่างไรและความซับซ้อนของรันไทม์ของฟังก์ชันรายการไพ ธ อนคืออะไร


push () ควรอยู่ใน O (log n) เนื่องจากรายการได้รับการจัดเรียงแล้ว
estani

1
อาจจะฉันควรจะได้กล่าวว่า"สำหรับสหกรณ์แทรก" ยังไงก็ประมาณหนึ่งปีที่แล้วตอนนี้ฉันสามารถผสมสิ่งต่างๆได้อย่างง่ายดายหรือพลาดอะไรไป
ジョージ

คุณสามารถแทรกค่าลงในรายการที่เรียงลำดับใน O (log n) ได้ตลอดเวลาดูการค้นหาแบบไบนารี push () หมายถึงการดำเนินการแทรก
estani

2
จริง แต่ในขณะที่การค้นหาตำแหน่งแทรกจะใช้ O (log n) ops จริง ๆ การแทรกจริง (เช่นการเพิ่มองค์ประกอบในโครงสร้างข้อมูล) อาจขึ้นอยู่กับโครงสร้างนั้น (คิดว่าการแทรกองค์ประกอบในอาร์เรย์ที่เรียงลำดับ) และเนื่องจากรายการ Python เป็นอาร์เรย์จริงๆจึงอาจใช้ O (n) เนื่องจากความคิดเห็นมีขนาด จำกัด ฉันจะเชื่อมโยงคำถาม SO ที่เกี่ยวข้องสองข้อจากข้อความของคำตอบ (ดูด้านบน)
ジョージ

โต้แย้งได้ดี ฉันไม่ทราบรายการที่จัดการเป็นอาร์เรย์ใน 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 (log n)ฉันจะบอกว่า ... ) มีหนึ่งข้อเสียคือ: การแยกรายการที่เรียงลำดับมีความซับซ้อนO (n log n)


เป็นเรื่องดี แต่ยากที่จะแบ่งเป็นสองส่วน
ilya n.

3
จะมีการทดสอบการเป็นสมาชิก O (log n) ในฮีปได้อย่างไร? หากคุณกำลังมองหาค่า x คุณสามารถหยุดมองหากิ่งไม้ได้หากคุณพบสิ่งที่มีขนาดใหญ่กว่า x แต่สำหรับค่าสุ่มของ x นั้นมีแนวโน้มที่จะอยู่ที่ใบไม้ 50% และคุณอาจไม่สามารถตัดได้มากนัก
ตลาด

1

ฉันจะใช้โมดูลbiscectหรือ sortedcontainersฉันไม่ค่อยมีประสบการณ์ แต่ฉันคิดว่าheapqโมดูลนี้ใช้งานได้ ประกอบด้วยไฟล์Heap Queue


0

การติดตั้ง sortlist ของคุณเองบน 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

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.