ฉันถูกถามคำถามนี้ระหว่างการสัมภาษณ์ พวกเขาทั้ง O (nlogn) และยังคนส่วนใหญ่ใช้ Quicksort แทน Mergesort ทำไมถึงเป็นอย่างนั้น?
"easier to hack a mergesort to do it than a quicksort"
ล่ะ ตัวอย่างเฉพาะใด ๆ ที่คุณสามารถอ้างอิงได้?
ฉันถูกถามคำถามนี้ระหว่างการสัมภาษณ์ พวกเขาทั้ง O (nlogn) และยังคนส่วนใหญ่ใช้ Quicksort แทน Mergesort ทำไมถึงเป็นอย่างนั้น?
"easier to hack a mergesort to do it than a quicksort"
ล่ะ ตัวอย่างเฉพาะใด ๆ ที่คุณสามารถอ้างอิงได้?
คำตอบ:
Quicksort มีการรันไทม์กรณีที่เลวร้ายที่สุดO ( n 2 ) และการรันไทม์เคสเฉลี่ยO ( n log n ) อย่างไรก็ตามมันยอดเยี่ยมกว่าที่จะรวมการเรียงลำดับในหลาย ๆ สถานการณ์เพราะมีหลายปัจจัยที่ส่งผลต่อรันไทม์ของอัลกอริทึมและเมื่อรวมเข้าด้วยกัน Quicksort จะชนะ
โดยเฉพาะอย่างยิ่งรันไทม์ของอัลกอริทึมการเรียงลำดับที่อ้างถึงมักอ้างถึงจำนวนการเปรียบเทียบหรือจำนวนของการแลกเปลี่ยนที่จำเป็นเพื่อดำเนินการเรียงลำดับข้อมูล นี่เป็นการวัดประสิทธิภาพที่ดีโดยเฉพาะอย่างยิ่งเนื่องจากไม่ขึ้นอยู่กับการออกแบบฮาร์ดแวร์พื้นฐาน อย่างไรก็ตามสิ่งอื่น ๆ เช่นสถานที่อ้างอิง (เช่นเราอ่านองค์ประกอบจำนวนมากที่อาจอยู่ในแคชหรือไม่) - มีบทบาทสำคัญกับฮาร์ดแวร์ปัจจุบัน โดยเฉพาะอย่างยิ่ง Quicksort ต้องการพื้นที่เพิ่มเติมเล็กน้อยและแสดงตำแหน่งแคชที่ดีและทำให้เร็วกว่าการรวมการเรียงลำดับในหลายกรณี
นอกจากนี้มันง่ายมากที่จะหลีกเลี่ยงเวลารันไทม์ที่เลวร้ายที่สุดของ Quicksort ของ O ( n 2 ) เกือบทั้งหมดโดยใช้ทางเลือกที่เหมาะสมของเดือย - เช่นเลือกโดยการสุ่ม (นี่เป็นกลยุทธ์ที่ยอดเยี่ยม)
ในทางปฏิบัติการใช้งานที่ทันสมัยจำนวนมากของ quicksort (โดยเฉพาะอย่างยิ่ง libstdc ++ std::sort
) เป็นจริงการใคร่ครวญซึ่งทฤษฎีกรณีที่เลวร้ายที่สุดคือ O ( n log n ) เช่นเดียวกับการเรียงผสาน มันประสบความสำเร็จด้วยการ จำกัด ระดับความลึก recursion และเปลี่ยนไปใช้ขั้นตอนวิธีการที่แตกต่างกัน ( heapsort ) เมื่อมันเกินบันทึกn
ดังที่หลายคนสังเกตเห็นว่าประสิทธิภาพของ case สำหรับ quicksort นั้นเร็วกว่าการผสาน แต่นี่เป็นเรื่องจริงหากคุณสมมติว่าเวลาคงที่ในการเข้าถึงหน่วยความจำที่ต้องการ
ใน RAM ข้อสันนิษฐานนี้โดยทั่วไปจะไม่เลวร้ายเกินไป (มันไม่ได้เป็นจริงเสมอเพราะแคช แต่ไม่เลวเกินไป) อย่างไรก็ตามถ้าโครงสร้างข้อมูลของคุณมีขนาดใหญ่พอที่จะอยู่บนดิสก์แล้ว quicksort จะถูกฆ่าเนื่องจากความจริงที่ว่าดิสก์เฉลี่ยของคุณทำสิ่งที่ต้องการ 200 สุ่มต่อวินาที แต่ดิสก์เดียวกันนั้นไม่มีปัญหาในการอ่านหรือเขียนเมกะไบต์ต่อวินาทีของข้อมูลตามลำดับ ซึ่งเป็นสิ่งที่การควบรวมกิจการทำ
ดังนั้นหากต้องจัดเรียงข้อมูลบนดิสก์คุณต้องการใช้รูปแบบบางอย่างในการรวมกัน (โดยทั่วไปคุณจะทำรายการย่อยอย่างรวดเร็วจากนั้นเริ่มรวมเข้าด้วยกันเหนือขีด จำกัด ขนาดบางส่วน)
นอกจากนี้ถ้าคุณต้องทำ อะไรกับชุดข้อมูลขนาดนั้นลองคิดหาวิธีหลีกเลี่ยงการหาดิสก์ ตัวอย่างเช่นนี่คือสาเหตุที่เป็นคำแนะนำมาตรฐานที่คุณวางดัชนีก่อนที่จะทำการโหลดข้อมูลจำนวนมากในฐานข้อมูลแล้วสร้างดัชนีใหม่ในภายหลัง การบำรุงรักษาดัชนีในระหว่างโหลดหมายถึงการค้นหาดิสก์อย่างต่อเนื่อง ในทางตรงกันข้ามถ้าคุณวางดัชนีจากนั้นฐานข้อมูลสามารถสร้างดัชนีใหม่โดยการเรียงลำดับข้อมูลที่จะจัดการ (โดยใช้การผสานแน่นอน!) แล้วโหลดลงในโครงสร้างข้อมูล BTREE สำหรับดัชนี (BTREE จะถูกเก็บไว้ตามธรรมชาติดังนั้นคุณสามารถโหลดหนึ่งชุดข้อมูลจากชุดข้อมูลที่มีการค้นหาน้อยไปยังดิสก์)
มีหลายครั้งที่การทำความเข้าใจกับวิธีการหลีกเลี่ยงการค้นหาดิสก์ทำให้ฉันต้องใช้เวลาในการประมวลผลข้อมูลมากกว่าชั่วโมงหรือหลายสัปดาห์
0
ต่อn
และครั้งต่อไปที่คุณไปจากที่มีต่อn
0
วิธีนี้จะทำให้ข้อดีของการถอยกลับ (การเรียงลำดับ) บล็อกข้อมูลที่มีอยู่แล้วในหน่วยความจำ (แคช) และการโจมตีสองครั้งสำหรับการเข้าถึงดิสก์เพียงครั้งเดียว ฉันคิดว่า DBMS ส่วนใหญ่ใช้เทคนิคการเพิ่มประสิทธิภาพนี้
ที่จริงแล้ว QuickSort คือ O (n 2 ) ใช้กรณีเฉลี่ยเวลาในการทำงานเป็น O (NLog (N)) แต่กรณีเลวร้ายที่สุดคือ O (n 2 ) ซึ่งเกิดขึ้นเมื่อคุณใช้มันในรายการว่ามีรายการที่ไม่ซ้ำกันไม่กี่ การสุ่มใช้เวลา O (n) แน่นอนว่านี่ไม่ใช่การเปลี่ยนแปลงกรณีที่เลวร้ายที่สุด แต่เพียงป้องกันผู้ใช้ที่ประสงค์ร้ายไม่ให้ทำการจัดเรียงของคุณใช้เวลานาน
QuickSort ได้รับความนิยมมากขึ้นเนื่องจาก:
"แต่คนส่วนใหญ่ใช้ Quicksort แทนที่จะเป็น Mergesort ทำไมถึงเป็นเช่นนั้น"
เหตุผลทางจิตวิทยาอย่างหนึ่งที่ไม่ได้รับก็คือ Quicksort นั้นตั้งชื่ออย่างชาญฉลาดมากขึ้น เช่นการตลาดที่ดี
ใช่ Quicksort ที่มีการแบ่งสามส่วนน่าจะเป็นหนึ่งในอัลกอริธึมการเรียงลำดับวัตถุประสงค์ทั่วไปที่ดีที่สุด แต่ไม่มีการเข้าใจว่าการเรียงลำดับ "ด่วน" ฟังดูมีประสิทธิภาพมากกว่าการเรียงลำดับ "ผสาน"
ดังที่คนอื่น ๆ ได้กล่าวไว้กรณีที่แย่ที่สุดของ Quicksort คือ O (n ^ 2) ในขณะที่การรวมและ heapsort จะอยู่ที่ O (nlogn) อย่างไรก็ตามโดยเฉลี่ยแล้วทั้งสามกรณีเป็น O (nlogn) ดังนั้นจึงเป็นกรณีส่วนใหญ่เทียบเคียง
สิ่งที่ทำให้ Quicksort ดีขึ้นโดยเฉลี่ยคือวงในหมายถึงการเปรียบเทียบค่าหลายค่ากับค่าเดียวในขณะที่อีกสองเงื่อนไขจะแตกต่างกันสำหรับการเปรียบเทียบแต่ละครั้ง กล่าวอีกนัยหนึ่ง Quicksort ทำครึ่งหนึ่งให้อ่านได้มากเท่ากับสองอัลกอริธึม เกี่ยวกับประสิทธิภาพของ CPU ที่ทันสมัยถูกครอบงำอย่างมากจากเวลาในการเข้าถึงดังนั้นในที่สุด Quicksort ก็กลายเป็นตัวเลือกแรกที่ดีเยี่ยม
ฉันต้องการเพิ่มที่สาม algoritms ที่กล่าวถึงแล้ว (การผสานการรวมแบบเร็วและเรียงลำดับฮีป) การผสานเพียงอย่างเดียวนั้นมีเสถียรภาพ นั่นคือลำดับจะไม่เปลี่ยนแปลงสำหรับค่าเหล่านั้นที่มีคีย์เดียวกัน ในบางกรณีนี่เป็นที่พึงปรารถนา
แต่ความจริงจะบอกว่าในสถานการณ์จริงคนส่วนใหญ่ต้องการเพียงประสิทธิภาพที่ดีโดยเฉลี่ยและ quicksort คือ ... quick =)
อัลกอริทึมการเรียงลำดับทั้งหมดมีอัพและดาวน์ของพวกเขา ดูบทความ Wikipedia สำหรับการจัดเรียงอัลกอริทึมสำหรับภาพรวมที่ดี
จากรายการ Wikipedia บน Quicksort :
Quicksort ยังแข่งขันกับการผสานรวม, อัลกอริทึมการเรียงลำดับแบบเรียกซ้ำ แต่ด้วยประโยชน์ของเวลาที่เลวร้ายที่สุด case (nlogn) Mergesort เป็นระบบจัดเรียงที่มีความเสถียรซึ่งแตกต่างจาก quicksort และ heapsort และสามารถปรับเปลี่ยนได้อย่างง่ายดายเพื่อใช้งานในรายการที่เชื่อมโยงและรายการขนาดใหญ่มากที่เก็บไว้ในสื่อที่เข้าถึงได้ช้าเช่นที่เก็บดิสก์หรือที่เก็บข้อมูลเครือข่าย แม้ว่า quicksort สามารถเขียนเพื่อใช้งานในรายการที่เชื่อมโยงได้ แต่บ่อยครั้งจะประสบกับตัวเลือกเดือยที่ไม่ดีโดยไม่ต้องเข้าถึงแบบสุ่ม ข้อเสียเปรียบหลักของการรวมกันคือเมื่อใช้งานบนอาร์เรย์มันต้องใช้พื้นที่เสริมΘ (n) ในกรณีที่ดีที่สุดในขณะที่ตัวแปรของ quicksort ที่มีการแบ่งพาร์ติชันและการเรียกใช้หางซ้ำใช้พื้นที่Θ (logn) เท่านั้น (โปรดทราบว่าเมื่อดำเนินการกับรายการที่เชื่อมโยงการรวมกันจะต้องมีที่เก็บข้อมูลสำรองจำนวนเล็กน้อยเท่านั้น)
หมู่! Quicksort นั้นไม่ดีกว่ามันเหมาะสำหรับการใช้งานประเภทอื่นมากกว่าการรวม
การควบรวมกิจการนั้นคุ้มค่าที่จะพิจารณาหากความเร็วเป็นสิ่งสำคัญประสิทธิภาพที่แย่ที่สุดในกรณีที่ไม่สามารถทนได้และมีพื้นที่เพิ่มเติมให้บริการ 1
คุณระบุว่าพวกเขา«พวกเขาทั้งคู่ O (nlogn) […] » นี่เป็นสิ่งที่ผิด « Quicksort ใช้การเปรียบเทียบ n ^ 2/2 ในกรณีที่เลวร้ายที่สุด» 1 .
อย่างไรก็ตามคุณสมบัติที่สำคัญที่สุดตามประสบการณ์ของฉันคือการใช้การเข้าถึงตามลำดับที่คุณสามารถใช้ในขณะที่เรียงลำดับเมื่อใช้ภาษาการเขียนโปรแกรมด้วยกระบวนทัศน์ที่จำเป็น
1 Sedgewick อัลกอริทึม
Quicksort เป็นอัลกอริทึมการเรียงลำดับที่เร็วที่สุดในทางปฏิบัติ แต่มีจำนวนกรณีทางพยาธิวิทยาที่สามารถทำให้มันทำงานได้ไม่ดีเท่า O (n2)
Heapsort รับประกันว่าจะทำงานใน O (n * ln (n)) และต้องการพื้นที่เก็บข้อมูลเพิ่มเติมที่ จำกัด เท่านั้น แต่มีการอ้างอิงจำนวนมากของการทดสอบในโลกแห่งความเป็นจริงซึ่งแสดงว่า heapsort ช้ากว่าการจัดเรียงโดยเฉลี่ยอย่างรวดเร็ว
คำอธิบายของ Wikipedia คือ:
โดยทั่วไปแล้ว quicksort จะเร็วกว่าในทางปฏิบัติมากกว่าอัลกอริธึมΘ (nlogn) อื่น ๆ เนื่องจาก loop ภายในสามารถนำไปใช้กับสถาปัตยกรรมส่วนใหญ่ได้อย่างมีประสิทธิภาพและในโลกแห่งความเป็นจริงก็เป็นไปได้ที่จะเลือกตัวเลือกการออกแบบ .
ฉันคิดว่ายังมีปัญหาเกี่ยวกับจำนวนของพื้นที่เก็บข้อมูลที่จำเป็นสำหรับการรวม (ซึ่งคือΩ (n)) ที่การใช้งาน quicksort ไม่ได้มี ในกรณีที่เลวร้ายที่สุดพวกมันมีระยะเวลาอัลกอริทึมเท่ากัน แต่การรวมกันนั้นต้องการพื้นที่จัดเก็บมากขึ้น
ฉันต้องการเพิ่มคำตอบที่ยอดเยี่ยมที่มีอยู่ในคณิตศาสตร์เกี่ยวกับประสิทธิภาพของ QuickSort เมื่อแยกจากกรณีที่ดีที่สุดและโอกาสที่จะเกิดขึ้นซึ่งฉันหวังว่าจะช่วยให้ผู้คนเข้าใจดีขึ้นเล็กน้อยว่าทำไมกรณี O (n ^ 2) ไม่ใช่ของจริง ข้อกังวลในการใช้งาน QuickSort ที่ซับซ้อนยิ่งขึ้น
นอกเหนือจากปัญหาการเข้าถึงแบบสุ่มมีสองปัจจัยหลักที่สามารถส่งผลกระทบต่อประสิทธิภาพการทำงานของ QuickSort และพวกเขาทั้งสองเกี่ยวข้องกับวิธีที่เดือยเปรียบเทียบกับข้อมูลที่ถูกเรียงลำดับ
1) คีย์จำนวนน้อยในข้อมูล ชุดข้อมูลที่มีค่าเดียวกันทั้งหมดจะเรียงลำดับใน n ^ 2 ครั้งบน QuickSort วานิลลาพาร์ติชัน 2 เนื่องจากค่าทั้งหมดยกเว้นตำแหน่ง pivot จะถูกวางไว้ที่ด้านหนึ่งในแต่ละครั้ง การใช้งานที่ทันสมัยอยู่นี้โดยวิธีการเช่นการใช้การเรียงลำดับ 3 พาร์ติชัน วิธีการเหล่านี้ดำเนินการในชุดข้อมูลที่มีค่าเดียวกันทั้งหมดในเวลา O (n) ดังนั้นการใช้การติดตั้งแบบนี้หมายความว่าอินพุตที่มีคีย์จำนวนน้อยจะช่วยเพิ่มประสิทธิภาพการทำงานได้จริงและไม่ต้องกังวลอีกต่อไป
2) การเลือกเดือยที่แย่มากอาจทำให้ประสิทธิภาพของเคสแย่ที่สุด ในกรณีที่เหมาะสาระสำคัญเดือยจะเป็นเช่นนั้นเสมอ 50% ข้อมูลมีขนาดเล็กและ 50% ข้อมูลใหญ่กว่าดังนั้นข้อมูลจะถูกแบ่งครึ่งในระหว่างการทำซ้ำแต่ละครั้ง สิ่งนี้ทำให้เราสามารถเปรียบเทียบและสลับเวลาได้แบบ log-2 (n) ซ้ำสำหรับเวลา O (n * logn)
การเลือกเดือยที่ไม่เหมาะจะส่งผลต่อเวลาดำเนินการเท่าไหร่
ลองพิจารณากรณีที่มีการเลือกเดือยอย่างสม่ำเสมอซึ่ง 75% ของข้อมูลอยู่ด้านหนึ่งของเดือย มันยังคงเป็น O (n * logn) แต่ตอนนี้ฐานของบันทึกได้เปลี่ยนเป็น 1 / 0.75 หรือ 1.33 ความสัมพันธ์ในการปฏิบัติงานเมื่อเปลี่ยนฐานเป็นค่าคงที่ที่แสดงโดย log (2) / log (newBase) เสมอ ในกรณีนี้ค่าคงที่นั้นคือ 2.4 ดังนั้นคุณภาพของตัวเลือกแบบเดือยจึงใช้เวลานานกว่าอุดมคติ
สิ่งนี้จะเลวร้ายยิ่งเร็วแค่ไหน?
ไม่เร็วมากจนกระทั่งตัวเลือก pivot ได้รับ (สม่ำเสมอ) แย่มาก:
ในขณะที่เราเข้าใกล้ 100% ในด้านหนึ่งส่วนบันทึกของการดำเนินการเข้าใกล้ n และการดำเนินการทั้งหมดเข้าใกล้ O (n ^ 2)
ในการใช้งาน QuickSort ที่ไร้เดียงสากรณีต่างๆเช่นอาร์เรย์ที่เรียงลำดับ (สำหรับเดือยองค์ประกอบที่ 1) หรืออาร์เรย์ที่เรียงกลับกัน (สำหรับเดือยองค์ประกอบสุดท้าย) จะสร้างเวลาดำเนินการที่เลวร้ายที่สุด O (n ^ 2) นอกจากนี้การใช้งานกับการเลือกเดือยที่คาดการณ์ได้อาจถูกโจมตีจาก DoS ด้วยข้อมูลที่ออกแบบมาเพื่อสร้างการประมวลผลกรณีที่เลวร้ายที่สุด การใช้งานที่ทันสมัยหลีกเลี่ยงปัญหานี้ด้วยวิธีการที่หลากหลายเช่นการสุ่มข้อมูลก่อนการเรียงลำดับการเลือกค่ามัธยฐานของ 3 ดัชนีที่เลือกแบบสุ่ม ฯลฯ ด้วยการสุ่มในการผสมนี้เรามี 2 กรณี:
มีโอกาสมากที่เราจะเห็นประสิทธิภาพแย่มาก?
โอกาสมีขนาดเล็กเต็มที ลองพิจารณาประเภท 5,000 ค่า:
การใช้สมมุติฐานของเราจะเลือกเดือยโดยใช้ค่ามัธยฐานของ 3 ดัชนีที่เลือกแบบสุ่ม เราจะพิจารณา pivots ที่อยู่ในช่วง 25% -75% ให้เป็น "ดี" และ pivots ที่อยู่ในช่วง 0% -25% หรือ 75% -100% เป็น "ไม่ดี" ถ้าคุณดูการแจกแจงความน่าจะเป็นโดยใช้ค่ามัธยฐานของดัชนีสุ่ม 3 ค่าการเรียกซ้ำแต่ละครั้งจะมีโอกาส 11/16 ในการจบด้วยเดือยที่ดี ให้เราตั้งสมมติฐานที่อนุรักษ์นิยม (และเท็จ) 2 ข้อเพื่อทำให้คณิตศาสตร์ง่ายขึ้น:
pivots ที่ดีจะอยู่ที่ 25% / 75% เสมอและใช้งานได้ในกรณีที่เหมาะสมที่สุด 2.4 * เราไม่เคยได้รับการแยกที่สมบูรณ์แบบหรือการแยกใด ๆ ที่ดีกว่า 25/75
pivots ที่ไม่ดีมักเป็นกรณีที่เลวร้ายที่สุดและไม่ได้มีส่วนช่วยอะไรเลย
การใช้งาน QuickSort ของเราจะหยุดที่ n = 10 และเปลี่ยนเป็นการเรียงลำดับการแทรกดังนั้นเราจึงต้องการพาร์ทิชันเดือย 22 25% / 75% เพื่อแบ่งอินพุต 5,000 ค่าจากจุดนั้น (10 * 1.333333 ^ 22> 5,000) หรือเราต้องการ pivots 4990 กรณีที่แย่ที่สุด โปรดจำไว้ว่าถ้าเราสะสม 22 เดือยที่ดี ณจุดใดการเรียงลำดับจะเสร็จสมบูรณ์ดังนั้นกรณีที่เลวร้ายที่สุดหรืออะไรก็ตามที่อยู่ใกล้มันต้องมีโชคร้ายมาก ถ้าเราใช้การเรียกซ้ำ 88 ครั้งเพื่อให้ได้ pivots 22 ตัวที่จำเป็นในการเรียงลำดับลงไปที่ n = 10 นั่นจะเป็นกรณีอุดมคติ 4 * 2.4 * หรือประมาณ 10 เท่าของเวลาดำเนินการของคดีอุดมคติ มีโอกาสเป็นไปได้มากน้อยเพียงใดที่เราจะไม่ได้รับ 22 เดือยที่ดีหลังจากการเรียกซ้ำ 88 ครั้ง
การแจกแจงความน่าจะเป็นแบบทวินามสามารถตอบได้และคำตอบคือประมาณ 10 ^ -18 (n คือ 88, k คือ 21, p คือ 0.6875) ผู้ใช้ของคุณมีแนวโน้มที่จะถูกฟ้าผ่าในเวลา 1 วินาทีที่จะคลิก [SORT] มากกว่าที่จะเห็นว่า 5,000 รายการเรียงลำดับที่แย่กว่านี้มากกว่า 10 * กรณีที่เหมาะ โอกาสนี้น้อยลงเมื่อชุดข้อมูลมีขนาดใหญ่ขึ้น นี่คือขนาดอาเรย์บางส่วนและโอกาสที่สอดคล้องกันในการรันนานกว่า 10 * ในอุดมคติ:
โปรดจำไว้ว่านี่เป็นไปตามสมมติฐานที่อนุรักษ์นิยม 2 ข้อที่เลวร้ายยิ่งกว่าความเป็นจริง ดังนั้นประสิทธิภาพที่แท้จริงจึงดีกว่าและความสมดุลของความน่าจะเป็นที่เหลืออยู่ใกล้เคียงกับอุดมคติมากกว่าไม่
ในที่สุดตามที่คนอื่น ๆ ได้กล่าวถึงแม้กรณีที่ไม่น่าเป็นไปได้เหล่านี้จะถูกกำจัดได้โดยการสลับไปเป็นกองซ้อนหากกองการเรียกซ้ำลึกเกินไป ดังนั้น TLDR ก็คือสำหรับการใช้งาน QuickSort ที่ดีกรณีที่เลวร้ายที่สุดไม่ได้เกิดขึ้นจริงเพราะมันได้รับการออกแบบทางวิศวกรรมและการดำเนินการเสร็จสมบูรณ์ในเวลา O (n * logn)
ทำไม Quicksort ถึงดี?
Quicksort ดีกว่า Mergesort เสมอหรือไม่
ไม่ได้จริงๆ
บันทึก:ใน java ฟังก์ชัน Arrays.sort () ใช้ Quicksort สำหรับชนิดข้อมูลดั้งเดิมและ Mergesort สำหรับชนิดข้อมูลวัตถุ เนื่องจากวัตถุใช้หน่วยความจำโอเวอร์เฮดดังนั้นการเพิ่มโอเวอร์เฮดเล็กน้อยสำหรับการรวมอาจไม่มีปัญหาใด ๆ สำหรับมุมมองประสิทธิภาพ
การอ้างอิง : ดูวิดีโอ QuickSort ของสัปดาห์ที่ 3 หลักสูตร Princeton Algorithms ที่ Coursera
Quicksort นั้นไม่ดีไปกว่าการรวมกัน ด้วย O (n ^ 2) (กรณีที่เลวร้ายที่สุดที่ไม่ค่อยเกิดขึ้น) quicksort อาจช้ากว่า O (nlogn) ของการเรียงรวม Quicksort มีค่าใช้จ่ายน้อยลงดังนั้นเมื่อใช้คอมพิวเตอร์ขนาดเล็กและช้าจะดีกว่า แต่คอมพิวเตอร์มีความรวดเร็วในวันนี้จนทำให้ค่าใช้จ่ายในการรวมกิจการเพิ่มขึ้นเล็กน้อยและความเสี่ยงของการทำธุรกรรมที่ช้ามากช้ากว่าค่าใช้จ่ายที่ไม่สำคัญของการรวมกิจการในกรณีส่วนใหญ่
นอกจากนี้การรวมกันยังทำให้รายการมีคีย์ที่เหมือนกันในลำดับเดิมซึ่งเป็นแอตทริบิวต์ที่มีประโยชน์
<=
จะใช้สำหรับการเปรียบเทียบมากกว่า<
และไม่มีเหตุผลที่จะไม่
คำตอบจะเอียงไปทาง quicksort wrt เล็กน้อยต่อการเปลี่ยนแปลงที่เกิดขึ้นด้วย DualPivotQuickSort สำหรับค่าดั้งเดิม มันถูกใช้ในJAVA 7เพื่อเรียงลำดับในjava.util.Arrays
It is proved that for the Dual-Pivot Quicksort the average number of
comparisons is 2*n*ln(n), the average number of swaps is 0.8*n*ln(n),
whereas classical Quicksort algorithm has 2*n*ln(n) and 1*n*ln(n)
respectively. Full mathematical proof see in attached proof.txt
and proof_add.txt files. Theoretical results are also confirmed
by experimental counting of the operations.
คุณสามารถค้นหาความหมายของ JAVA7 ได้ที่นี่ - http://grepcode.com/file/repository.grepcode.com/java/root/jdk/openjdk/7-b147/java/util/Arrays.java
การอ่านที่ยอดเยี่ยมเพิ่มเติมเกี่ยวกับ DualPivotQuickSort - http://permalink.gmane.org/gmane.comp.java.openjdk.core-libs.devel/2628
ในการจัดเรียงผสานอัลกอริทึมทั่วไปคือ:
ที่ระดับบนสุดการรวม 2 อาร์เรย์ย่อยที่เรียงลำดับเกี่ยวข้องกับการจัดการกับองค์ประกอบ N
หนึ่งระดับที่ต่ำกว่านั้นการวนซ้ำของขั้นตอนที่ 3 เกี่ยวข้องกับการจัดการกับองค์ประกอบ N / 2 แต่คุณต้องทำซ้ำกระบวนการนี้สองครั้ง ดังนั้นคุณยังคงต้องรับมือกับองค์ประกอบ 2 * N / 2 == N
หนึ่งระดับที่ต่ำกว่านั้นคุณกำลังรวมองค์ประกอบ 4 * N / 4 == N องค์ประกอบและอื่น ๆ ความลึกทุกครั้งในสแต็กแบบเรียกซ้ำเกี่ยวข้องกับการรวมจำนวนองค์ประกอบเดียวกันในทุกการโทรสำหรับความลึกนั้น
พิจารณาอัลกอริทึมการเรียงลำดับอย่างรวดเร็วแทน:
ที่ระดับบนสุดคุณกำลังจัดการกับอาร์เรย์ที่มีขนาด N จากนั้นคุณเลือกจุดหมุนหนึ่งจุดวางไว้ในตำแหน่งที่ถูกต้อง
หนึ่งระดับที่ต่ำกว่านั้นคุณกำลังจัดการกับ 2 อาร์เรย์ย่อยที่มีขนาดรวมของ N-1 (เช่นลบจุดหมุนก่อนหน้านี้) คุณเลือกจุดหมุนสำหรับแต่ละแถวย่อยซึ่งมีจุดหมุนเพิ่มเติม 2 จุด
หนึ่งระดับที่ต่ำกว่านั้นคุณกำลังจัดการกับ 4 แถวย่อยที่มีขนาดรวม N-3 ด้วยเหตุผลเดียวกับข้างต้น
จากนั้น N-7 ... จากนั้น N-15 ... จากนั้น N-32 ...
ความลึกของสแต็กแบบวนซ้ำของคุณยังคงอยู่โดยประมาณ (logN) ด้วยการผสานการจัดเรียงคุณจะต้องจัดการกับการรวมองค์ประกอบ N ตลอดทุกระดับของสแต็กแบบเรียกซ้ำ ด้วยการจัดเรียงอย่างรวดเร็วจำนวนขององค์ประกอบที่คุณจัดการกับลดลงเมื่อคุณลงไปในกอง ตัวอย่างเช่นถ้าคุณดูที่ความลึกตรงกลางผ่านสแต็กแบบเรียกซ้ำจำนวนขององค์ประกอบที่คุณติดต่อด้วยคือ N - 2 ^ ((logN) / 2)) == N - sqrt (N)
ข้อจำกัดความรับผิดชอบ: ในการเรียงลำดับเนื่องจากคุณแบ่งอาร์เรย์ออกเป็น 2 ชิ้นเท่ากันในแต่ละครั้งความลึกแบบเรียกซ้ำคือ logN ในการจัดเรียงอย่างรวดเร็วเนื่องจากจุดหมุนของคุณไม่น่าจะอยู่ตรงกลางของอาเรย์ความลึกของสแต็กแบบวนซ้ำอาจสูงกว่า logN เล็กน้อย ฉันยังไม่ได้ทำคณิตศาสตร์เพื่อดูว่าบทบาทนี้มีขนาดใหญ่เพียงใดและปัจจัยที่อธิบายไว้ข้างต้นมีบทบาทในความซับซ้อนของอัลกอริทึม
ไม่เหมือนกับ Merge Sort Quick Sort ไม่ได้ใช้พื้นที่เสริม โดยที่ Merge Sort ใช้ช่องว่างเสริม O (n) แต่ Merge Sort มีความซับซ้อนของเวลากรณีที่เลวร้ายที่สุดของ O (nlogn) ในขณะที่ความซับซ้อนของกรณีที่แย่ที่สุดของ Quick Sort คือ O (n ^ 2) ซึ่งเกิดขึ้นเมื่ออาร์เรย์เรียงลำดับแล้ว
Quicksort มีความซับซ้อนของกรณีโดยเฉลี่ยที่ดีกว่า แต่ในบางแอปพลิเคชันเป็นตัวเลือกที่ผิด Quicksort เสี่ยงต่อการถูกปฏิเสธการโจมตีบริการ หากผู้โจมตีสามารถเลือกอินพุตที่จะเรียงลำดับเขาสามารถสร้างชุดที่ใช้เวลาที่ซับซ้อนที่สุดของ o (n ^ 2) ได้อย่างง่ายดาย
ความซับซ้อนของกรณีโดยเฉลี่ยของ Mergesort และความซับซ้อนของกรณีที่แย่ที่สุดนั้นเท่ากันและสิ่งนี้ไม่ได้ประสบปัญหาเดียวกัน คุณสมบัติการผสานการจัดเรียงนี้ยังทำให้เป็นตัวเลือกที่ยอดเยี่ยมสำหรับระบบเรียลไทม์ - แม่นยำเพราะไม่มีกรณีทางพยาธิวิทยาที่ทำให้มันทำงานช้าลงมาก
ฉันเป็นแฟนตัวยงของการควบรวมกิจการมากกว่าที่ฉันเป็น Quicksort ด้วยเหตุผลเหล่านี้
มันยากที่จะบอกว่า MergeSort ที่แย่ที่สุดคือ n (log2n) -n + 1 ซึ่งถูกต้องถ้า n เท่ากับ 2 ^ k (ฉันได้พิสูจน์แล้ว) และสำหรับ n ใด ๆ มันอยู่ระหว่าง (n lg n - n + 1) และ (n lg n + n + O (lg n)) แต่สำหรับ quickSort วิธีที่ดีที่สุดคือ nlog2n (เช่น n เท่ากับ 2 ^ k) หากคุณหารการรวมกันโดย quickSort จะเท่ากับหนึ่งเมื่อ n ไม่มีที่สิ้นสุดดังนั้น มันเหมือนกับว่ากรณีที่แย่ที่สุดของ MergeSort นั้นดีกว่ากรณีที่ดีที่สุดของ QuickSort ทำไมเราถึงใช้ quicksort แต่จำไว้ว่า MergeSort ไม่ได้อยู่ในสถานที่มันต้องการพื้นที่ 2n memeroy และ MergeSort ก็ต้องทำสำเนาอาเรย์มากมาย ไม่รวมอยู่ในการวิเคราะห์อัลกอริทึมในคำ MergeSort จริงๆ faseter มากกว่า quicksort ใน theroy แต่ในความเป็นจริงคุณต้องพิจารณาพื้นที่ memeory ค่าใช้จ่ายของการคัดลอกอาร์เรย์การควบรวมกิจการจะช้ากว่าการเรียงลำดับอย่างรวดเร็ว การทดลองที่ฉันได้รับ 1000000 หลักใน java โดยชั้นเรียนแบบสุ่มและใช้เวลารวม 2610 มิลลิวินาทีโดยการรวมกัน, 1370 มิลลิวินาทีโดยการดูด
การเรียงลำดับด่วนเป็นกรณีที่เลวร้ายที่สุด O (n ^ 2) อย่างไรก็ตามกรณีเฉลี่ยออกมาอย่างสม่ำเสมอจะทำการเรียงลำดับการผสาน อัลกอริทึมแต่ละตัวคือ O (nlogn) แต่คุณต้องจำไว้ว่าเมื่อพูดถึง Big O เราจะปล่อยให้ปัจจัยความซับซ้อนต่ำลง การจัดเรียงอย่างรวดเร็วมีการปรับปรุงที่สำคัญมากกว่าการรวมการเรียงเมื่อมันมาถึงปัจจัยคงที่
การจัดเรียงเวียนยังต้องใช้หน่วยความจำ O (2n) ในขณะที่การเรียงลำดับอย่างรวดเร็วสามารถทำได้ในสถานที่ (ต้องการเพียง O (n)) นี่คืออีกเหตุผลที่โดยทั่วไปแล้วการเรียงแบบด่วนจะเป็นที่นิยมมากกว่าการเรียงแบบผสาน
ข้อมูลเสริม:
กรณีที่เลวร้ายที่สุดของการจัดเรียงอย่างรวดเร็วเกิดขึ้นเมื่อมีการเลือกเดือยไม่ดี ลองพิจารณาตัวอย่างต่อไปนี้:
[5, 4, 3, 2, 1]
หากเลือกเดือยเป็นจำนวนที่เล็กที่สุดหรือใหญ่ที่สุดในกลุ่มการจัดเรียงอย่างรวดเร็วจะทำงานใน O (n ^ 2) ความน่าจะเป็นที่จะเลือกองค์ประกอบที่มีค่ามากที่สุดหรือเล็กที่สุด 25% ของรายการคือ 0.5 นั่นทำให้อัลกอรึทึมมีโอกาส 0.5 ในการเป็นเดือยที่ดี ถ้าเราใช้อัลกอริทึมการเลือกเดือยทั่วไป (พูดว่าการเลือกองค์ประกอบแบบสุ่ม) เรามีโอกาส 0.5 ที่จะเลือกเดือยที่ดีสำหรับตัวเลือกของเดือยทุกตัว สำหรับคอลเลกชันที่มีขนาดใหญ่ความน่าจะเป็นที่จะเลือกเดือยที่ไม่ดีคือ 0.5 * n การจัดเรียงอย่างรวดเร็วน่าจะเป็นนี้มีประสิทธิภาพสำหรับกรณีเฉลี่ย (และทั่วไป)
นี่เป็นคำถามที่ค่อนข้างเก่า แต่เนื่องจากเมื่อเร็ว ๆ นี้ฉันได้รับการตอบโต้ทั้งสองข้อนี่คือ 2c ของฉัน:
การเรียงแบบผสานต้องการการเปรียบเทียบโดยเฉลี่ย ~ N บันทึก N สำหรับอาร์เรย์ที่เรียงลำดับแล้ว (เกือบ) ที่เรียงลำดับแล้วสิ่งนี้จะลดลงเหลือ 1/2 N บันทึก N เนื่องจากในขณะที่รวมเรา (เกือบ) จะเลือกส่วน "ซ้าย" เสมอ 1/2 N ครั้งแล้วคัดลอกองค์ประกอบ 1/2 N ที่ถูกต้อง นอกจากนี้ฉันสามารถคาดเดาได้ว่าอินพุตที่เรียงลำดับแล้วทำให้ตัวทำนายสาขาของโปรเซสเซอร์เปล่งประกาย แต่คาดเดาได้เกือบทุกสาขาอย่างถูกต้องจึงป้องกันการวางแผงไปป์ไลน์
การเรียงลำดับแบบด่วนโดยเฉลี่ยต้องใช้การเปรียบเทียบ ~ 1.38 N บันทึก N มันไม่ได้ประโยชน์อย่างมากจากการเรียงอาเรย์แล้วในแง่ของการเปรียบเทียบ (แต่มันทำในแง่ของการแลกเปลี่ยนและอาจเป็นในแง่ของการพยากรณ์สาขาในซีพียู)
มาตรฐานของฉันเกี่ยวกับโปรเซสเซอร์ที่ทันสมัยพอสมควรแสดงดังต่อไปนี้:
เมื่อฟังก์ชั่นการเปรียบเทียบเป็นฟังก์ชั่นการโทรกลับ (เช่นใน qsort () การใช้ libc) Quicksort จะช้ากว่าการรวม 15% จากการสุ่มอินพุตและ 30% สำหรับอาร์เรย์ที่เรียงลำดับแล้วสำหรับจำนวนเต็ม 64 บิต
ในทางกลับกันถ้าการเปรียบเทียบไม่ใช่การโทรกลับประสบการณ์ของฉันคือการที่ quicksort มีประสิทธิภาพสูงกว่าการรวมกันมากถึง 25%
อย่างไรก็ตามถ้าอาร์เรย์ (ขนาดใหญ่) ของคุณมีค่าที่ไม่ซ้ำกันมากการเรียงแบบผสานจะเริ่มได้รับการตอบสนองเร็วในทุกกรณี
ดังนั้นบรรทัดล่างคือ: ถ้าการเปรียบเทียบมีราคาแพง (เช่นฟังก์ชันการโทรกลับการเปรียบเทียบสตริงการเปรียบเทียบหลายส่วนของโครงสร้างส่วนใหญ่จะเป็นแบบที่สองในสาม "ถ้า" เพื่อสร้างความแตกต่าง) - โอกาสที่คุณจะดีกว่า ด้วยการเรียงลำดับผสาน สำหรับงานที่ง่ายกว่าจะเร็วกว่าด่วน
ที่กล่าวไว้ก่อนหน้านี้ทั้งหมดบอกว่าเป็นจริง: - Quicksort สามารถเป็น N ^ 2 แต่ Sedgewick อ้างว่าการใช้งานแบบสุ่มที่ดีนั้นมีโอกาสมากขึ้นที่คอมพิวเตอร์จะทำการเรียงลำดับที่ถูกฟ้าผ่ามากกว่าที่จะไป N ^ 2 - Mergesort ต้องการพื้นที่เพิ่มเติม
เมื่อฉันทดลองกับอัลกอริธึมการเรียงลำดับทั้งสองโดยการนับจำนวนการโทรซ้ำแบบเรียกซ้ำการโทรแบบเร็วจะมีการโทรซ้ำแบบเรียกซ้ำน้อยกว่าการรวมแบบซ้ำ เป็นเพราะ quicksort มี pivots และ pivots ไม่รวมอยู่ในการโทรซ้ำครั้งถัดไป วิธีนี้ Quicksort สามารถเข้าถึงกรณีพื้นฐานแบบเรียกซ้ำได้เร็วกว่าการรวมแบบซ้ำ
นี่เป็นคำถามทั่วไปที่ถามในการสัมภาษณ์ว่าแม้ประสิทธิภาพของกรณีการเรียงแบบผสานที่แย่ที่สุด แต่ Quicksort ก็ถือว่าดีกว่าการรวมแบบเรียงลำดับโดยเฉพาะอย่างยิ่งสำหรับการป้อนข้อมูลขนาดใหญ่ มีเหตุผลบางอย่างเนื่องจากการ quicksort ดีกว่า:
1- พื้นที่เสริม: การจัดเรียงอย่างรวดเร็วเป็นอัลกอริทึมการเรียงลำดับแบบแทนที่ การเรียงลำดับแบบ in-place หมายถึงไม่จำเป็นต้องใช้พื้นที่เก็บข้อมูลเพิ่มเติมในการเรียงลำดับ การเรียงลำดับการผสานในทางกลับกันจำเป็นต้องมีอาร์เรย์ชั่วคราวเพื่อรวมอาร์เรย์ที่เรียงแล้วดังนั้นจึงไม่ได้อยู่ในตำแหน่ง
2- กรณีที่แย่ที่สุด: กรณีที่แย่ที่สุดของ quicksort O(n^2)
สามารถหลีกเลี่ยงได้โดยใช้ quicksort แบบสุ่ม สามารถหลีกเลี่ยงได้อย่างง่ายดายด้วยความน่าจะเป็นสูงโดยเลือกเดือยที่เหมาะสม การได้รับพฤติกรรมของเคสโดยเฉลี่ยโดยการเลือกองค์ประกอบเดือยที่ถูกต้องจะทำให้การทำงานนั้นมีประสิทธิภาพและมีประสิทธิภาพเท่ากับการผสานการจัดเรียง
3- ตำแหน่งของการอ้างอิง: Quicksort โดยเฉพาะจะแสดงตำแหน่งแคชที่ดีและทำให้เร็วกว่าการรวมการเรียงลำดับในหลายกรณีเช่นในสภาพแวดล้อมของหน่วยความจำเสมือน
4- การเรียกซ้ำแบบหาง: QuickSort จะเป็นการวนแบบวนซ้ำในขณะที่การเรียงแบบผสานไม่ใช่ ฟังก์ชัน recursive แบบหางเป็นฟังก์ชันที่การเรียกแบบเรียกซ้ำเป็นสิ่งสุดท้ายที่ดำเนินการโดยฟังก์ชัน ฟังก์ชั่นการเรียกซ้ำแบบหางถือว่าดีกว่าฟังก์ชั่นการเรียกซ้ำแบบไม่หางเนื่องจากการเรียกซ้ำแบบหางสามารถปรับให้เหมาะสมโดยคอมไพเลอร์
ในขณะที่พวกเขาทั้งคู่อยู่ในระดับความซับซ้อนเดียวกันนั่นไม่ได้หมายความว่าพวกเขาทั้งสองมีรันไทม์เดียวกัน Quicksort มักจะเร็วกว่าการผสานเพียงเพราะมันง่ายต่อการเขียนโค้ดการใช้งานที่แน่นหนาและการดำเนินการที่ทำได้เร็วกว่า เป็นเพราะ quicksort นั้นโดยทั่วไปเร็วกว่าที่คนใช้แทนการผสาน
แต่! โดยส่วนตัวแล้วฉันมักจะใช้การผสานหรือการแยกประเภทอย่างรวดเร็วที่ลดระดับลงเป็นการรวมตัวเมื่อการเรียงลำดับด่วนไม่ดี จำ quicksort เป็นเพียง O (n log n) บนเฉลี่ย มันเป็นกรณีที่แย่ที่สุดคือ O (n ^ 2)! การรวมกันเป็น O เสมอ (n log n) ในกรณีที่จำเป็นต้องใช้ประสิทธิภาพการตอบสนองแบบเรียลไทม์และข้อมูลอินพุตของคุณอาจมาจากแหล่งที่เป็นอันตรายคุณไม่ควรใช้ quicksort ธรรมดา
ทุกสิ่งเท่าเทียมกันฉันคาดหวังว่าคนส่วนใหญ่จะใช้สิ่งที่มีอยู่ให้สะดวกที่สุดและนั่นก็คือ qsort (3) นอกเหนือจาก quicksort นั้นเป็นที่รู้กันว่ารวดเร็วมากในอาร์เรย์เช่นเดียวกับการผสานคือตัวเลือกทั่วไปสำหรับรายการ
สิ่งที่ฉันสงสัยคือเหตุผลว่าทำไมจึงยากที่จะเห็นการเรียงลำดับแบบradixหรือ bucket พวกเขากำลัง O (n) อย่างน้อยในรายการที่เชื่อมโยงและสิ่งที่ต้องทำคือวิธีการแปลงคีย์ให้เป็นเลขลำดับ (สตริงและลอยทำงานได้ดี)
ฉันคิดว่าเหตุผลเกี่ยวข้องกับการสอนวิทยาศาสตร์คอมพิวเตอร์ ฉันต้องแสดงให้อาจารย์เห็นในการวิเคราะห์อัลกอริทึมว่าเป็นไปได้จริง ๆ ที่จะเรียงลำดับเร็วกว่า O (n log (n)) (เขามีหลักฐานว่าคุณไม่สามารถเปรียบเทียบการเรียงลำดับได้เร็วกว่า O (n log (n)) ซึ่งเป็นความจริง)
ในข่าวอื่น ๆ ลอยสามารถจัดเรียงเป็นจำนวนเต็ม แต่คุณต้องเปลี่ยนจำนวนลบรอบหลังจากนั้น
แก้ไข: อันที่จริงนี่เป็นทางที่ชั่วร้ายมากขึ้นในการจัดเรียงลอย as-จำนวนเต็ม: http://www.stereopsis.com/radix.html โปรดทราบว่าเคล็ดลับการพลิกบิตสามารถใช้โดยไม่คำนึงถึงอัลกอริทึมการเรียงลำดับที่คุณใช้จริง ...
qsort
การรวมแบบผสาน
ส่วนเพิ่มเติมเล็ก ๆ น้อย ๆ เทียบกับการผสานอย่างรวดเร็ว
นอกจากนี้ยังสามารถขึ้นอยู่กับประเภทของการเรียงลำดับรายการ หากการเข้าถึงไอเท็มการสลับและการเปรียบเทียบไม่ใช่การดำเนินการอย่างง่ายเช่นการเปรียบเทียบจำนวนเต็มในหน่วยความจำแบบระนาบดังนั้นการเรียงแบบผสานอาจเป็นวิธีที่ดีกว่า
ตัวอย่างเช่นเราจัดเรียงรายการโดยใช้โปรโตคอลเครือข่ายบนเซิร์ฟเวอร์ระยะไกล
นอกจากนี้ในคอนเทนเนอร์แบบกำหนดเองเช่น "รายการที่ลิงก์" ประโยชน์ของการจัดเรียงแบบรวดเร็ว
1. รวมการเรียงลำดับในรายการที่เชื่อมโยงไม่ต้องการหน่วยความจำเพิ่มเติม 2. การเข้าถึงองค์ประกอบในการเรียงลำดับด่วนไม่ได้เรียงตามลำดับ (ในหน่วยความจำ)
Quick sort เป็นอัลกอริธึมการเรียงลำดับแบบ in-place ดังนั้นจึงเหมาะสำหรับอาร์เรย์ การเรียงแบบผสานในทางกลับกันต้องใช้พื้นที่จัดเก็บพิเศษของ O (N) และเหมาะสำหรับรายการที่เชื่อมโยง
ซึ่งแตกต่างจากอาร์เรย์ในรายการที่ชอบเราสามารถแทรกรายการตรงกลางด้วยพื้นที่ O (1) และเวลา O (1) ดังนั้นการดำเนินการผสานในการจัดเรียงผสานสามารถดำเนินการได้โดยไม่ต้องมีพื้นที่เพิ่มเติม อย่างไรก็ตามการปันส่วนและการจัดสรรพื้นที่เพิ่มเติมสำหรับอาร์เรย์มีผลกระทบในเวลาทำงานของการผสานการเรียงลำดับ การเรียงลำดับการผสานยังสนับสนุนรายการที่เชื่อมโยงเมื่อเข้าถึงข้อมูลได้ตามลำดับ
ในขณะที่การเรียงลำดับอย่างรวดเร็วนั้นจำเป็นต้องใช้การเข้าถึงหน่วยความจำแบบสุ่มจำนวนมากและด้วยอาเรย์เราสามารถเข้าถึงหน่วยความจำโดยตรงได้โดยไม่ต้องทำการสำรวจตามที่ต้องการโดยรายการที่ลิงก์ การเรียงลำดับอย่างรวดเร็วเมื่อใช้สำหรับอาร์เรย์มีตำแหน่งอ้างอิงที่ดีเนื่องจากอาร์เรย์ถูกเก็บไว้ในหน่วยความจำอย่างต่อเนื่อง
แม้ว่าอัลกอริธึมการเรียงลำดับทั้งสองจะมีความซับซ้อนโดยเฉลี่ยคือ O (NlogN) แต่โดยทั่วไปคนสำหรับงานทั่วไปใช้อาเรย์สำหรับการจัดเก็บและด้วยเหตุนี้การเรียงลำดับอย่างรวดเร็วควรเป็นอัลกอริธึมที่เลือก
แก้ไข: ฉันเพิ่งพบว่าการเรียงลำดับการจัดเรียงที่แย่ที่สุด / ดีที่สุด / avg เป็น nlogn เสมอ แต่การเรียงลำดับอย่างรวดเร็วอาจแตกต่างจาก n2 (กรณีที่แย่ที่สุดเมื่อองค์ประกอบเรียงลำดับแล้ว) เป็น nlogn (กรณีเฉลี่ย / ที่ดีที่สุดเมื่อ pivot หารอาร์เรย์ในสองเสมอ แบ่งเท่า ๆ กัน)
พิจารณาความซับซ้อนของเวลาและสถานที่ทั้งสอง สำหรับการเรียงแบบผสาน: ความซับซ้อนของเวลา: O (nlogn), ความซับซ้อนของพื้นที่: O (nlogn)
สำหรับการเรียงลำดับด่วน: ความซับซ้อนของเวลา: O (n ^ 2), ความซับซ้อนของพื้นที่: O (n)
ตอนนี้พวกเขาทั้งสองชนะในหนึ่งฉากแต่ละฉาก แต่การใช้เดือยแบบสุ่มคุณสามารถลดความซับซ้อนของเวลาในการเรียงลำดับแบบด่วนเป็น O (nlogn) ได้เกือบทุกครั้ง
ดังนั้นการเรียงแบบด่วนจึงเป็นที่ต้องการในหลาย ๆ แอปพลิเคชั่นแทนที่จะเป็น Merge sort
ใน c / c ++ ที่ดินเมื่อไม่ได้ใช้คอนเทนเนอร์ stl ฉันมักจะใช้ quicksort เพราะมันถูกสร้างขึ้นในเวลาทำงานในขณะที่การรวมกันไม่ได้
ดังนั้นฉันเชื่อว่าในหลาย ๆ กรณีมันเป็นเพียงเส้นทางของการต่อต้านน้อยที่สุด
นอกจากนี้ประสิทธิภาพการทำงานอาจสูงขึ้นด้วยการจัดเรียงอย่างรวดเร็วสำหรับกรณีที่ชุดข้อมูลทั้งหมดไม่พอดีกับชุดการทำงาน
qsort
เป็นการผสานการจัดเรียงเว้นแต่ว่าองค์ประกอบจะใหญ่โตอย่างแท้จริงหรือไม่สามารถจัดสรรหน่วยความจำชั่วคราวได้ cvs.savannah.gnu.org/viewvc/libc/stdlib/ …
หนึ่งในเหตุผลคือปรัชญามากขึ้น Quicksort คือ Top-> Down ปรัชญา ด้วยองค์ประกอบ n เรียงลำดับมี n! ความเป็นไปได้ ด้วยสองส่วนของ m & nm ซึ่งไม่เหมือนกันซึ่งกันและกันจำนวนของความเป็นไปได้ลดลงตามลำดับความสำคัญหลายประการ m! * (nm)! มีขนาดเล็กโดยคำสั่งหลายกว่า n! คนเดียว ลองนึกภาพ 5! กับ 3! * 2 !. 5! มีความเป็นไปได้ 10 เท่ามากกว่า 2 พาร์ติชั่น 2 และ 3 ในแต่ละส่วน และประมาณ 1 ล้านแฟคทอเรียลเทียบกับ 900K! * 100K! vs. ดังนั้นแทนที่จะกังวลเกี่ยวกับการสร้างใบสั่งใด ๆ ภายในช่วงหรือพาร์ติชันเพียงแค่สร้างใบสั่งในระดับที่กว้างขึ้นในพาร์ติชันและลดความเป็นไปได้ภายในพาร์ติชัน คำสั่งซื้อใด ๆ ที่จัดตั้งขึ้นก่อนหน้านี้ภายในขอบเขตจะถูกรบกวนในภายหลังหากพาร์ติชันไม่ได้เกิดร่วมกัน
การเรียงลำดับจากล่างขึ้นบนใด ๆ เช่นการผสานการเรียงลำดับหรือการเรียงลำดับกองซ้อนเป็นเหมือนวิธีการทำงานของพนักงานหรือพนักงานที่เริ่มเปรียบเทียบในระดับจุลทรรศน์ แต่เนิ่นๆ แต่คำสั่งนี้จะหายไปทันทีที่องค์ประกอบในระหว่างนั้นถูกพบในภายหลัง วิธีการเหล่านี้มีความเสถียรและคาดการณ์ได้อย่างมาก แต่ต้องทำงานเพิ่มจำนวนหนึ่ง
การจัดเรียงด่วนเป็นเหมือนวิธีการจัดการที่ไม่มีใครสนใจในตอนแรกเกี่ยวกับคำสั่งใด ๆ เพียงเกี่ยวกับการประชุมเกณฑ์กว้าง ๆ โดยไม่คำนึงถึงคำสั่งซื้อ จากนั้นพาร์ติชันจะถูกลดขนาดลงจนกว่าคุณจะได้ชุดเรียงลำดับ ความท้าทายที่แท้จริงใน Quicksort คือการหาพาร์ติชันหรือเกณฑ์ในที่มืดเมื่อคุณไม่รู้อะไรเกี่ยวกับองค์ประกอบที่จะเรียงลำดับ นั่นคือเหตุผลที่เราต้องใช้ความพยายามเพื่อหาค่ามัธยฐานหรือเลือก 1 แบบสุ่มหรือวิธีการ "จัดการ" ตามอำเภอใจ เพื่อหาค่ามัธยฐานที่สมบูรณ์สามารถใช้ความพยายามอย่างมากและนำไปสู่วิธีการจากล่างขึ้นบนงี่เง่าอีกครั้ง ดังนั้น Quicksort กล่าวเพียงแค่เลือกเดือยสุ่มและหวังว่ามันจะอยู่ตรงกลางหรือทำงานเพื่อหาค่ามัธยฐานของ 3, 5 หรือมากกว่าเพื่อหาค่ามัธยฐานที่ดีกว่า แต่ไม่ได้วางแผนที่จะสมบูรณ์แบบ & ไม่ ' ไม่ต้องเสียเวลาในการสั่งซื้อครั้งแรก ดูเหมือนว่าจะทำได้ดีถ้าคุณโชคดีหรือบางครั้งลดน้อยลงเป็น n ^ 2 เมื่อคุณไม่ได้รับค่ามัธยฐาน แต่ใช้โอกาส ข้อมูลใด ๆ ที่เป็นแบบสุ่ม ขวา. ดังนั้นฉันจึงเห็นด้วยกับวิธีการทางด้านบนของตรรกะแบบเร็ว -> และปรากฎว่ามีโอกาสที่จะเลือก pivot & การเปรียบเทียบที่บันทึกไว้ก่อนหน้านี้ดูเหมือนว่าจะทำงานได้ดีกว่าเวลาด้านล่างที่พิถีพิถันและมีเสถียรภาพ -> เรียงลำดับการผสาน แต่ การเปรียบเทียบว่าจะช่วยประหยัดก่อนหน้านี้ดูเหมือนว่าจะทำงานได้ดีขึ้นกว่าช่วงล่างที่มีความพิถีพิถันและมีเสถียรภาพ -> วิธีการขึ้นเช่นการผสาน แต่ การเปรียบเทียบว่าจะช่วยประหยัดก่อนหน้านี้ดูเหมือนว่าจะทำงานได้ดีขึ้นกว่าช่วงล่างที่มีความพิถีพิถันและมีเสถียรภาพ -> วิธีการขึ้นเช่นการผสาน แต่
qsort
, Pythonlist.sort
, และArray.prototype.sort
JavaScript ของ Firefox นั้นทุกอย่างผสานเข้าด้วยกัน (GNU STLsort
ใช้ Introsort แทน แต่ที่อาจจะเป็นเพราะใน C ++ แลกเปลี่ยนอาจชนะที่ยิ่งใหญ่กว่าการคัดลอก.)