เมนูนำทาง
แถวลำดับพลวัต หลักการทำงานแนวคิดเบื้องต้นของแถวลำดับพลวัตเริ่มต้นด้วยการจองหน่วยความจำพลวัตเพื่อสร้างแถวลำดับขนาดคงที่ขึ้นมาให้มีขนาดในระดับหนึ่ง เรียกขนาดนี้ว่าความจุของแถวลำดับพลวัต แถวลำดับที่สร้างมานี้จะประกอบไปด้วยสองส่วนคือ ส่วนที่เก็บข้อมูลของแถวลำดับพลวัต กับ ส่วนที่ไม่ได้ใช้งาน ขนาดของส่วนที่เก็บข้อมูลนี้เรียกว่าขนาดของแถวลำดับพลวัต เมื่อมีการเพิ่มข้อมูลเข้ามาต่อท้ายก็เป็นเพียงการเปลี่ยนส่วนที่ไม่ได้ใช้งานให้มาเป็นส่วนเก็บข้อมูล ในขณะที่การลบข้อมูลออกด้านท้ายก็เป็นการเปลี่ยนส่วนเก็บข้อมูลให้มาเป็นส่วนที่ไม่ได้ใช้งาน ซึ่งการดำเนินการเปลี่ยนพื้นที่ไปมาระหว่าง ส่วนที่เก็บข้อมูล กับ ส่วนที่ไม่ได้ใช้งาน ใช้เวลาคงที่หรือ Θ ( 1 ) {\displaystyle \Theta (1)} เท่านั้น
ตราบใดที่ขนาดของแถวลำดับพลวัตมีค่าน้อยกว่าหรือเท่ากับความจุของแถวลำดับพลวัตก็จะไม่มีปัญหาใด ๆ เกิดขึ้น แต่ถ้าหากเกิดเหตุการณ์ที่มีการเพิ่มข้อมูลจนทำให้ขนาดของแถวลำดับพลวัตมีค่าเกินความจุของแถวลำดับพลวัต สิ่งที่ต้องทำก็คือการขยายความจุ โดยการสร้างแถวลำดับขนาดคงที่อันใหม่ให้มีขนาดมากกว่าเดิม (มีความจุมากกว่าความจุเดิม) และคัดลอกข้อมูลจากแถวลำดับขนาดคงที่อันเดิมไปแถวลำดับขนาดคงที่อันใหม่ การดำเนินการขยายความจุนี้เสียเวลามาก กล่าวคือหากมีข้อมูลอยู่ n {\displaystyle n} ตัวในแถวลำดับพลวัต การขยายความจุนี้จะใช้เวลาเชิงเส้น หรือ Θ ( n ) {\displaystyle \Theta (n)}
รูปแบบการทำงานของแถวลำดับพลวัต อาจแบ่งได้ออกเป็น 3 แบบ คือ
ในกรณีที่ทราบว่าแถวลำดับพลวัตจะเก็บข้อมูลเท่าใด สามารถกำหนดความจุให้เป็นค่าคงที่หนึ่งที่มีค่ามากกว่าหรือเท่ากับจำนวนของข้อมูล เพื่อรับประกันว่าจะไม่มีปัญหาที่ขนาดของแถวลำดับพลวัตมีค่าเกินความจุของแถวลำดับพลวัต ซึ่งนำไปสู่การเสียเวลาจากการขยายความจุ นอกจากนี้ เนื่องจากความจุเป็นค่าคงที่ พื้นที่ที่สูญเสียไปโดยไม่จำเป็นก็เป็นค่าคงที่เช่นเดียวกัน อย่างไรก็ตาม ในทางปฏิบัติมักจะไม่รู้ถึงจำนวนของข้อมูลที่แน่นอน และการเก็บข้อมูลด้วยแถวลำดับพลวัตที่ความจุเป็นค่าคงที่ในบางสถานการณ์ กลับทำให้สูญเสียพื้นที่โดยไม่จำเป็นอย่างมากมาย เช่นหากมีข้อมูล n {\displaystyle n} ตัวที่จะนำมาเก็บบนรายการ m {\displaystyle m} รายการตามคำสั่งที่กำหนด จะได้ว่ารายการหนึ่ง ๆ มีโอกาสที่จะมีข้อมูลมากสุดถึง n {\displaystyle n} ตัว ดังนั้นหากใช้แถวลำดับพลวัตที่ความจุเป็นค่าคงที่ในการทำรายการทั้ง m {\displaystyle m} รายการนั้น ก็ต้องหน่วยความจำถึง Θ ( n m ) {\displaystyle \Theta (nm)} เพื่อรับประกันว่าข้อมูลจะสามารถเก็บได้หมด แต่ถ้าหากใช้แถวลำดับพลวัตที่ความจุไม่เป็นค่าคงที่ (หัวข้อถัด ๆ ไป) จะใช้หน่วยความจำเพียง Θ ( n + m ) {\displaystyle \Theta (n+m)} เท่านั้น
เมื่อไม่ทราบจำนวนข้อมูลที่แน่นอน สิ่งที่อาจจะเกิดขึ้นได้ตลอดเวลาก็คือปัญหาที่ขนาดของแถวลำดับพลวัตมีค่าเกินความจุของแถวลำดับพลวัต ตามที่ได้กล่าวมาแล้วว่าวิธีการแก้ไขปัญหานี้ก็คือการขยายความจุ ในหัวข้อนี้ จะให้การขยายความจุเพิ่มความจุในแต่ละครั้งเป็นค่าคงที่ c
ครั้งที่ของการขยายความจุ | 1 {\displaystyle 1} | 2 {\displaystyle 2} | 3 {\displaystyle 3} | ... | k {\displaystyle k} | สรุป | |
---|---|---|---|---|---|---|---|
ขนาดของแถวลำดับพลวัตในรอบนั้น ๆ | c {\displaystyle c} | 2 × c {\displaystyle 2\times c} | 3 × c {\displaystyle 3\times c} | ... | k × c {\displaystyle k\times c} | ขนาดสุดท้าย | k × c {\displaystyle k\times c} |
จำนวนคำสั่งในการคัดลอกข้อมูลในรอบนั้น ๆ | c {\displaystyle c} | 2 × c {\displaystyle 2\times c} | 3 × c {\displaystyle 3\times c} | ... | k × c {\displaystyle k\times c} | รวมจำนวนคำสั่ง | ( 1 + 2 + 3 + . . . + k ) × c {\displaystyle (1+2+3+...+k)\times c} |
จากตาราง เมื่อมีการใส่ข้อมูลเข้ามาในแถวลำดับพลวัตเป็นจำนวน n = k × c {\displaystyle n=k\times c} ตัว จะมีการทำงานจากการจองหน่วยความจำและคัดลอกข้อมูลถึง ( 1 + 2 + . . . + k ) × c = Θ ( k 2 ) {\displaystyle (1+2+...+k)\times c=\Theta (k^{2})} ครั้ง เนื่องจาก k ∝ n {\displaystyle k\propto n} จะได้ว่าการใส่ข้อมูลเข้าไป n {\displaystyle n} ตัวใช้เวลาถึง Θ ( n 2 ) {\displaystyle \Theta (n^{2})} ถึงแม้วิธีนี้จะเสียเวลามาก แต่พื้นที่ที่สูญเสียไปก็คือส่วนที่ยังไม่ได้ใช้ซึ่งเป็นเพียงค่าคงที่เท่านั้น อย่างไรก็ตาม ในทางปฏิบัติมักจะคำนึงถึงความรวดเร็วของการทำงานมากกว่าพื้นที่ที่สูญเสียไป
เพื่อหลีกเลี่ยงการจองหน่วยความจำและคัดลอกข้อมูลหลายครั้งเกินความจำเป็น จึงมีแนวคิดใหม่ให้การขยายความจุเพิ่มความจุใหม่ในแต่ละครั้งเป็น c เท่า ซึ่งจะทำให้ความจุเพิ่มขึ้นเป็นอัตราเรขาคณิต รหัสเทียมแสดงการทำงานเป็นไปตามนี้
function insertEnd (dynarray a, element e) if (a.size = a.capacity) // resize a to c times of its current capacity: a.capacity ← a.capacity * c // (copy the contents to the new memory location here) a[a.size] ← e a.size ← a.size + 1
เมื่อมีการใส่ข้อมูลตัวที่ n {\displaystyle n} เข้ามาและเกิดการขยายเกิดขึ้น จะมีการจองหน่วยความจำและคัดลอกข้อมูลซึ่งใช้เวลา Θ ( n ) {\displaystyle \Theta (n)} ความจุจะเพิ่มขึ้น c เท่า นั่นหมายความว่าเมื่อพิจารณาถึงจำนวนคำสั่งที่ใช้รวมทั้งหมด ความถี่ในการขยายขนาดในแต่ละครั้งจะห่างขึ้นเรื่อย ๆ เป็นอัตราเรขาคณิต ทำให้เมื่อวิเคราะห์แบบถัวเฉลี่ย การเพิ่มข้อมูลที่บางครั้ง Θ ( 1 ) {\displaystyle \Theta (1)} บางครั้ง Θ ( n ) {\displaystyle \Theta (n)} จะสามารถถัวเฉลี่ยให้ทุก ๆ การดำเนินการกลายเป็น Θ ( 1 ) {\displaystyle \Theta (1)} ได้ ตารางข้างล่างนี้แสดงถึงการขยายความจุที่ความจุเพิ่มเป็นอัตราเรขาคณิต
ครั้งที่ของการขยายขนาด | 1 {\displaystyle 1} | 2 {\displaystyle 2} | 3 {\displaystyle 3} | ... | k {\displaystyle k} | สรุป | |
---|---|---|---|---|---|---|---|
ขนาดของแถวลำดับพลวัตในรอบนั้น ๆ | 1 {\displaystyle 1} | c {\displaystyle c} | c 2 {\displaystyle c^{2}} | ... | c k − 1 {\displaystyle c^{k-1}} | ขนาดสุดท้าย | c k − 1 {\displaystyle c^{k-1}} |
จำนวนคำสั่งในการคัดลอกข้อมูลในรอบนั้น ๆ | 1 {\displaystyle 1} | c {\displaystyle c} | c 2 {\displaystyle c^{2}} | ... | c k − 1 {\displaystyle c^{k-1}} | รวมจำนวนคำสั่ง | 1 + c + c 2 + . . . + c k − 1 {\displaystyle 1+c+c^{2}+...+c^{k-1}} |
สรุปได้ว่าเมื่อมีการใส่ข้อมูลเข้ามาในแถวลำดับพลวัตเป็นจำนวน n = c k − 1 {\displaystyle n=c^{k-1}} ตัว จะมีการทำงาน 1 + c + c 2 + . . . + c k − 1 = Θ ( c k ) {\displaystyle 1+c+c^{2}+...+c^{k-1}=\Theta (c^{k})} ครั้ง เนื่องจาก k ∝ log c n {\displaystyle k\propto \log _{c}n} จะได้ว่าการใส่ข้อมูลเข้าไป n {\displaystyle n} ตัวใช้เวลา Θ ( c log c n ) = Θ ( n ) {\displaystyle \Theta (c^{\log _{c}n})=\Theta (n)} ดังนั้นจึงอาจถัวเฉลี่ยให้การดำเนินการเพิ่มข้อมูลครั้งหนึ่งใช้เวลา Θ ( 1 ) {\displaystyle \Theta (1)} ได้ ข้อเสียของแถวลำดับพลวัตที่ขยายความจุเป็นอัตราเรขาคณิตคือพื้นที่ที่เสียไปโดยไม่จำเป็นอาจมีมากถึงเชิงเส้น O ( n ) {\displaystyle O(n)} [1]
ค่า c นั้นสามารถเลือกใช้เป็นจำนวนใด ๆ ก็ได้ที่มากกว่า 1 เพื่อให้เห็นภาพได้ชัด หนังสือส่วนใหญ่ใช้ค่า c = 2 [3][4] แต่เพื่อให้พื้นที่ที่เสียไปโดยไม่จำเป็นไม่มากนัก ในทางปฏิบัติมักจะใช้ค่า c ที่น้อยกว่านั้น เช่น ArrayList
ของภาษาจาวาใช้ค่า c = 3/2 [2] list
ของภาษาไพธอน (ซึ่งเขียนด้วยภาษา C) ใช้ค่า c = 9/8[5]
แถวลำดับพลวัตเป็นตัวอย่างที่นิยมใช้ในการสอนเรื่องการวิเคราะห์แบบถัวเฉลี่ย[3][4]
เมนูนำทาง
แถวลำดับพลวัต หลักการทำงานใกล้เคียง
แถวลำดับพลวัต แถวลำดับแบบจับคู่ แถวลำดับ แถวลำดับเส้นฐานยาวมาก แถวลำดับจูดี้ แถวลำดับขนาดใหญ่มาก แถวลำดับแอลซีพี แถวลำดับซัฟฟิกซ์ แถวลำดับแบบขนาน แถบลำดับหลักแหล่งที่มา
WikiPedia: แถวลำดับพลวัต http://www.cs.uwaterloo.ca/research/tr/1999/09/CS-... http://www.collectionspy.com http://www.cplusplus.com/reference/deque/deque/ http://www.cplusplus.com/reference/vector/vector/ http://www.ddj.com/architect/184409965?pgno=5 http://channel9.msdn.com/Events/GoingNative/GoingN... http://download.oracle.com/javase/7/docs/api/java/... http://kjellkod.wordpress.com/2012/02/25/why-you-s... http://www.juniata.edu/faculty/kruse/cs240/linkedl... http://www.juniata.edu/faculty/kruse/cs240/syllabu...