ทำไมฉันถึงชอบใช้เวกเตอร์เพื่อ deque


87

ตั้งแต่

  1. ทั้งคู่เป็นคอนเทนเนอร์หน่วยความจำที่ต่อเนื่องกัน
  2. คุณลักษณะที่ชาญฉลาด deque มีเวกเตอร์เกือบทุกอย่าง แต่มีมากกว่านั้นเนื่องจากการแทรกที่ด้านหน้ามีประสิทธิภาพ

ทำไมทุกคน Whould ต้องการstd::vectorเพื่อstd::deque?


2
การใช้ Visual C ++ std::dequeมีขนาดบล็อกสูงสุดที่เล็กมาก (~ 16 ไบต์ถ้าฉันจำได้ถูกต้องอาจเป็น 32) และด้วยเหตุนี้จึงทำงานได้ไม่ดีนักสำหรับแอปพลิเคชันที่สมจริง โดยdeque<T>ที่sizeof(T) > 8(หรือ 16 เป็นตัวเลขเล็ก ๆ ) มีลักษณะการทำงานเหมือนกับ a vector<T*>โดยที่แต่ละองค์ประกอบได้รับการจัดสรรแบบไดนามิก การใช้งานอื่น ๆ dequeที่มีขนาดของบล็อกสูงสุดแตกต่างกันดังนั้นการเขียนโค้ดที่มีค่อนข้างลักษณะการทำงานเดียวกันบนแพลตฟอร์มที่แตกต่างเป็นเรื่องยากกับ
James McNellis

13
Deque ไม่ใช่ที่เก็บหน่วยความจำต่อเนื่อง
Mohamed El-Nakib

@ravil ไม่นั่นคือรายการที่ซ้ำกันชี้ไปที่คำถามนี้

1
ยากที่จะเชื่อว่าคำถามที่มีข้อผิดพลาดที่เป็นข้อเท็จจริงที่ไม่ชัดเจนกำลังนั่งอยู่ที่คะแนนโหวต 34 คะแนน
underscore_d

2
@underscore_d นั่นจึงเป็นคำถาม คนละเรื่องถ้าเป็นคำตอบ;)
Assimilater

คำตอบ:


115

องค์ประกอบในdequeมีความไม่ต่อเนื่องกันในหน่วยความจำ vectorองค์ประกอบได้รับการประกันว่าเป็น ดังนั้นหากคุณต้องการที่จะมีปฏิสัมพันธ์กับห้องสมุด C ธรรมดาที่ต้องการอาร์เรย์ที่ต่อเนื่องกันหรือถ้าคุณสนใจ (มาก) vectorเกี่ยวกับท้องที่อวกาศแล้วคุณอาจจะชอบ นอกจากนี้เนื่องจากมีการทำบัญชีพิเศษบางหน่วยงานอื่น ๆ อาจมีราคาแพงกว่าvectorการดำเนินการที่เทียบเท่ากัน (เล็กน้อย) ในทางกลับกันการใช้อินสแตนซ์จำนวนมาก / จำนวนมากvectorอาจทำให้เกิดการกระจายตัวของฮีปโดยไม่จำเป็น (ทำให้การโทรช้าลงnew)

นอกจากนี้ยังเป็นแหลมออกอื่น ๆ ใน StackOverflowมีการสนทนาที่ดีอื่น ๆ อีกมากมายที่นี่: http://www.gotw.ca/gotw/054.htm


3
ดูเหมือนว่าตอนนี้ลิงก์ไปยัง "ที่อื่น" จะตาย (เนื่องจากการกลั่นกรอง?)
esilk

37

หากต้องการทราบความแตกต่างเราควรทราบวิธีdequeการนำไปใช้โดยทั่วไป หน่วยความจำถูกจัดสรรเป็นบล็อกที่มีขนาดเท่ากันและจะถูกรวมเข้าด้วยกัน (เป็นอาร์เรย์หรืออาจเป็นเวกเตอร์)

ดังนั้นหากต้องการค้นหาองค์ประกอบที่ n คุณจะพบบล็อกที่เหมาะสมจากนั้นเข้าถึงองค์ประกอบที่อยู่ภายในนั้น นี่เป็นเวลาคงที่เพราะมันมักจะค้นหา 2 ครั้ง แต่นั่นก็ยังมากกว่าเวกเตอร์

vectorยังทำงานได้ดีกับ API ที่ต้องการบัฟเฟอร์ที่ต่อเนื่องกันเนื่องจากเป็น C API หรือมีความหลากหลายมากกว่าในการใช้ตัวชี้และความยาว (ดังนั้นคุณสามารถมีเวกเตอร์อยู่ข้างใต้หรืออาร์เรย์ปกติและเรียก API จากบล็อกหน่วยความจำของคุณ)

ในกรณีที่dequeมีข้อได้เปรียบที่ใหญ่ที่สุดคือ:

  1. เมื่อขยายหรือหดคอลเลกชันจากปลายทั้งสองข้าง
  2. เมื่อคุณต้องจัดการกับคอลเลกชันขนาดใหญ่มาก
  3. เมื่อจัดการกับบูลและคุณต้องการบูลมากกว่าบิตเซ็ต

อย่างที่สองเป็นที่รู้จักน้อยกว่า แต่สำหรับคอลเลกชันขนาดใหญ่มาก:

  1. ต้นทุนในการจัดสรรใหม่มีจำนวนมาก
  2. ค่าใช้จ่ายในการค้นหาบล็อกหน่วยความจำที่ต่อเนื่องกันนั้นมีข้อ จำกัด ดังนั้นคุณจึงสามารถใช้หน่วยความจำได้เร็วขึ้น

เมื่อฉันจัดการกับคอลเลกชันขนาดใหญ่ในอดีตและย้ายจากโมเดลที่ต่อเนื่องกันไปเป็นโมเดลบล็อกเราสามารถจัดเก็บคอลเลคชันขนาดใหญ่ได้ประมาณ 5 เท่าก่อนที่หน่วยความจำจะหมดในระบบ 32 บิต ส่วนหนึ่งเป็นเพราะเมื่อทำการจัดสรรใหม่จำเป็นต้องจัดเก็บบล็อกเก่าและบล็อกใหม่ก่อนที่จะคัดลอกองค์ประกอบ

เมื่อพูดทั้งหมดนี้คุณอาจมีปัญหากับstd::dequeระบบที่ใช้การจัดสรรหน่วยความจำแบบ "มองโลกในแง่ดี" ในขณะที่ความพยายามที่จะขอขนาดบัฟเฟอร์ขนาดใหญ่สำหรับการจัดสรรใหม่ของ a vectorอาจจะถูกปฏิเสธในบางจุดด้วย a bad_allocลักษณะที่มองโลกในแง่ดีของผู้จัดสรรมักจะให้การร้องขอสำหรับบัฟเฟอร์ขนาดเล็กที่ร้องขอโดย a dequeและมีแนวโน้มที่จะก่อให้เกิด ระบบปฏิบัติการจะฆ่ากระบวนการเพื่อพยายามรับหน่วยความจำบางส่วน เลือกอันไหนก็อาจจะไม่ถูกใจนัก

วิธีแก้ปัญหาในกรณีดังกล่าว ได้แก่ การตั้งค่าแฟล็กระดับระบบเพื่อลบล้างการจัดสรรในแง่ดี (ไม่สามารถทำได้เสมอไป) หรือจัดการหน่วยความจำด้วยตนเองค่อนข้างมากขึ้นเช่นการใช้ตัวจัดสรรของคุณเองที่ตรวจสอบการใช้หน่วยความจำหรือสิ่งที่คล้ายกัน เห็นได้ชัดว่าไม่เหมาะอย่างยิ่ง (ซึ่งอาจตอบคำถามของคุณได้ว่าชอบเวกเตอร์ ... )


30

ฉันใช้ทั้ง vector และ deque หลายครั้งแล้ว deque มีความซับซ้อนมากขึ้นอย่างมากจากมุมมองของการนำไปใช้งาน ความซับซ้อนนี้แปลเป็นรหัสมากขึ้นและรหัสที่ซับซ้อนมากขึ้น ดังนั้นโดยทั่วไปคุณจะเห็นขนาดโค้ดเมื่อคุณเลือก deque ทับเวกเตอร์ นอกจากนี้คุณยังอาจได้รับการตีด้วยความเร็วเพียงเล็กน้อยหากโค้ดของคุณใช้เฉพาะสิ่งที่เวกเตอร์เก่งเท่านั้น

หากคุณต้องการคิวสองครั้ง deque คือผู้ชนะที่ชัดเจน แต่ถ้าคุณใช้ส่วนแทรกส่วนใหญ่แล้วลบที่ด้านหลังเวกเตอร์จะเป็นผู้ชนะที่ชัดเจน เมื่อคุณไม่แน่ใจให้ประกาศคอนเทนเนอร์ของคุณด้วย typedef (เพื่อให้ง่ายต่อการสลับไปมา) และวัดผล


3
คำถาม - คณะกรรมการได้พิจารณาเพิ่มไฮบริดของทั้งสอง (เช่น "เด็ค") ให้กับ C ++ หรือไม่? (เช่นสองสิ้นสุดลงvector.) ฉันได้เขียนการดำเนินการเชื่อมโยงไปยังด้านล่างในคำตอบของฉัน สามารถทำได้เร็วพอ ๆ กับ a vectorแต่ใช้ได้อย่างกว้างขวางกว่ามาก (เช่นเมื่อทำการต่อคิวอย่างรวดเร็ว)
user541686

5

std::dequeไม่ได้รับประกันหน่วยความจำต่อเนื่อง - และมักจะค่อนข้างช้ากว่าสำหรับการเข้าถึงแบบดัชนี โดยทั่วไปจะใช้ deque เป็น "รายการเวกเตอร์"


14
ฉันคิดว่า "รายการเวกเตอร์" ไม่ถูกต้อง: ความเข้าใจของฉันคือการใช้งานส่วนใหญ่เป็น "เวกเตอร์ของตัวชี้ไปยังอาร์เรย์" แม้ว่าจะขึ้นอยู่กับคำจำกัดความของ "รายการ" ของคุณ (ฉันอ่าน "รายการ" เป็น "รายการที่เชื่อมโยง "ซึ่งจะไม่เป็นไปตามข้อกำหนดด้านความซับซ้อน)
James McNellis

2

อ้างอิงจากhttp://www.cplusplus.com/reference/stl/deque/ "ซึ่งแตกต่างจากเวกเตอร์คือไม่รับประกันว่า deques จะมีองค์ประกอบทั้งหมดในสถานที่จัดเก็บที่ต่อเนื่องกันทำให้ไม่สามารถเข้าถึงได้อย่างปลอดภัยโดยใช้เลขคณิตตัวชี้"

Deques ซับซ้อนกว่าเล็กน้อยส่วนหนึ่งเป็นเพราะพวกเขาไม่จำเป็นต้องมีเค้าโครงหน่วยความจำที่ต่อเนื่องกัน หากคุณต้องการคุณสมบัตินั้นคุณไม่ควรใช้ deque

(ก่อนหน้านี้คำตอบของฉันแสดงให้เห็นถึงการขาดมาตรฐาน (จากแหล่งข้อมูลเดียวกันกับข้างต้น "deques อาจถูกนำไปใช้โดยไลบรารีเฉพาะในรูปแบบที่แตกต่างกัน") แต่จริงๆแล้วใช้กับประเภทข้อมูลไลบรารีมาตรฐานเท่านั้น)


5
std::dequestd::vectorไม่น้อยกว่ามาตรฐาน ฉันไม่เชื่อว่าข้อกำหนดด้านความซับซ้อนstd::dequeจะเป็นไปตามที่จัดเก็บข้อมูลที่ต่อเนื่องกันได้
Jerry Coffin

1
บางทีการใช้ถ้อยคำของฉันก็ไม่ดีแม้ว่าจะเป็นความจริงที่ว่าการกำหนดมาตรฐานจะไม่ทั่วถึงอย่างที่ฉันเข้าใจ แต่เวกเตอร์ได้รับการกำหนดมาตรฐานให้เป็นลำดับที่สอดคล้องกันและการกระทำนั้นไม่ได้เป็นมาตรฐาน นั่นดูเหมือนจะเป็นปัจจัยหนึ่งในการตัดสินใจ
pattivacek

1
@JerryCoffin: ข้อกำหนดด้านความซับซ้อนใดที่dequeไม่สามารถรองรับกับพื้นที่เก็บข้อมูลที่ต่อเนื่องกันได้?
541686

1
@ Mehrdad: พูดตามตรงฉันจำไม่ได้ว่ามีอะไรอยู่ในใจ เมื่อเร็ว ๆ นี้ฉันยังไม่ได้ดูส่วนนั้นของมาตรฐานมากพอที่ฉันรู้สึกสบายใจที่จะระบุอย่างชัดเจนว่าความคิดเห็นก่อนหน้าของฉันผิด แต่เมื่อมองในตอนนี้ฉันก็คิดไม่ออกว่ามันจะถูกต้องอย่างไร
Jerry Coffin

3
@JerryCoffin: ข้อกำหนดด้านความซับซ้อนนั้นไม่สำคัญจริงๆ: คุณสามารถจัดสรรอาร์เรย์ขนาดใหญ่และเริ่มผลักดันลำดับของคุณจากตรงกลางออกไปด้านนอก (ฉันเดาว่านี่คือสิ่งที่การใช้งาน Mehrdad ทำ) จากนั้นจัดสรรใหม่เมื่อคุณไปถึงจุดสิ้นสุด ปัญหาของแนวทางนี้คือไม่เป็นไปตามข้อกำหนดข้อใดข้อหนึ่งdequeกล่าวคือการแทรกที่ปลายจะไม่ทำให้การอ้างอิงถึงองค์ประกอบที่มีอยู่เป็นโมฆะ ข้อกำหนดนี้แสดงถึงหน่วยความจำที่ไม่ต่อเนื่อง
Yakov Galka

0

deque เป็นคอนเทนเนอร์ลำดับที่อนุญาตให้เข้าถึงองค์ประกอบโดยสุ่ม แต่ไม่รับประกันว่าจะมีพื้นที่จัดเก็บที่ต่อเนื่อง


0

ฉันคิดว่าเป็นความคิดที่ดีที่จะทำการทดสอบประสิทธิภาพของแต่ละกรณี และตัดสินใจโดยอาศัยการทดสอบนี้

ฉันชอบstd::dequeมากกว่าstd::vectorในกรณีส่วนใหญ่


1
คำถามถ้าใด ๆ ที่สามารถกลั่นจากบรรดาข้อผิดพลาดจริงและคำที่หายไปคือเหตุผลที่vectorคนต้องการ เราสามารถสรุปได้ว่าเหตุใดจึงไม่เป็นข้อพิสูจน์ การบอกว่าคุณชอบdequeโดยไม่ทราบสาเหตุจากการทดสอบที่ไม่ระบุรายละเอียดไม่ใช่คำตอบ
underscore_d

0

คุณไม่ต้องการให้เวกเตอร์ลบล้างตามผลการทดสอบเหล่านี้ (พร้อมแหล่งที่มา)

แน่นอนคุณควรทดสอบในแอป / สภาพแวดล้อมของคุณ แต่โดยสรุป:

  • push_back นั้นเหมือนกันสำหรับทุกคน
  • แทรกลบใน deque เร็วกว่า list มากและเร็วกว่าเวกเตอร์เล็กน้อย

ข้อมูลเพิ่มเติมบางส่วนและหมายเหตุที่ควรพิจารณา Circular_buffer


0

ในแง่หนึ่งเวกเตอร์มักจะค่อนข้างเร็วกว่า deque หากคุณไม่ต้องการคุณสมบัติทั้งหมดของ deque ให้ใช้เวกเตอร์

ในทางกลับกันบางครั้งคุณทำคุณลักษณะจำเป็นซึ่งเวกเตอร์ไม่ได้ให้คุณในกรณีที่คุณต้องใช้ deque ตัวอย่างเช่นฉันขอท้าให้ทุกคนพยายามเขียนโค้ดนี้ใหม่โดยไม่ต้องใช้ deque และไม่ต้องแก้ไขอัลกอริทึมมหาศาล


ที่จริงแล้วในซีรีย์push_backและpop_backการดำเนินการเดียวกันdeque<int>นั้นเร็วกว่าvector<int>การทดสอบของฉันอย่างน้อย 20% เสมอ(gcc กับ O3) ฉันเดาว่านั่นคือเหตุผลที่dequeเป็นตัวเลือกมาตรฐานสำหรับสิ่งต่างๆเช่นstd::stack...
igel

-1

โปรดทราบว่าหน่วยความจำเวกเตอร์จะถูกจัดสรรใหม่เมื่ออาร์เรย์เติบโตขึ้น หากคุณมีตัวชี้ไปยังองค์ประกอบเวกเตอร์องค์ประกอบเหล่านั้นจะไม่ถูกต้อง

นอกจากนี้หากคุณลบองค์ประกอบตัวทำซ้ำจะไม่ถูกต้อง (แต่ไม่ใช่ "สำหรับ (อัตโนมัติ ... )")

แก้ไข: เปลี่ยน 'deque' เป็น 'vector'


-1: การแทรกและลบที่จุดเริ่มต้นหรือจุดสิ้นสุดไม่ทำให้การอ้างอิงและตัวชี้ไปยังองค์ประกอบอื่น ๆ เป็นโมฆะ (แม้ว่าจะแทรกและลบตรงกลางก็ตาม)
Sjoerd

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