วิธี Hoare - การเรียงลำดับด่วน Quicksort การใช้งาน Quicksort ค

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

มีโปรแกรมเมอร์จำนวนหนึ่งที่ใช้เวลาทำงานทั้งหมดเพื่อศึกษาและใช้อัลกอริธึมการเรียงลำดับ เนื่องจากซอฟต์แวร์ธุรกิจส่วนใหญ่เกี่ยวข้องกับการจัดการฐานข้อมูล ผู้คนค้นหาข้อมูลในฐานข้อมูลตลอดเวลา ซึ่งหมายความว่าอัลกอริทึมการค้นหาเป็นที่ต้องการสูง

แต่มี "แต่" อย่างหนึ่ง อัลกอริธึมการค้นหาทำงานเร็วขึ้นมากกับฐานข้อมูลที่จัดเรียงไว้แล้ว ในกรณีนี้ ต้องใช้การค้นหาเชิงเส้นเท่านั้น

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

บทความนี้แสดงตัวอย่างการใช้งานอัลกอริธึมการเรียงลำดับมาตรฐาน

เรียงลำดับการเลือก

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

รหัสซี++

เป็นโมฆะ SortAlgo :: selectionSort (int data, int lenD) ( int j = 0; int tmp = 0; สำหรับ (int i = 0; i ข้อมูล[k])( เจ = k; ) ) tmp = ข้อมูล [i];

ข้อมูล [i] = ข้อมูล [j];

ข้อมูล[j] = tmp;

รหัสซี++

- =(i+1);j--)( ถ้า (ข้อมูล[j]

การเรียงลำดับการแทรก

การเรียงลำดับการแทรกจะแยกอาร์เรย์ออกเป็นสองขอบเขต: เรียงลำดับและไม่เรียงลำดับ ในตอนแรก อาเรย์ทั้งหมดจะเป็นขอบเขตที่ไม่เรียงลำดับ ในการผ่านครั้งแรก องค์ประกอบแรกจากขอบเขตที่ไม่ได้เรียงลำดับจะถูกลบออก และวางในตำแหน่งที่ถูกต้องในภูมิภาคที่ได้รับคำสั่ง

ในแต่ละรอบ ขนาดของขอบเขตที่ได้รับคำสั่งจะเพิ่มขึ้น 1 และขนาดของขอบเขตที่ไม่เป็นระเบียบจะลดลง 1

วงหลักเริ่มจาก 1 ถึง N-1 ในการวนซ้ำที่ j องค์ประกอบ [i] จะถูกแทรกเข้าไปในตำแหน่งที่ถูกต้องในภูมิภาคที่ได้รับคำสั่ง ทำได้โดยการเลื่อนองค์ประกอบทั้งหมดของขอบเขตการเรียงลำดับที่มากกว่า [i] ไปทางขวาหนึ่งตำแหน่ง [i] ถูกแทรกลงในช่องว่างระหว่างองค์ประกอบที่น้อยกว่า [i] และองค์ประกอบที่มากกว่า [i]

รหัสซี++

เป็นโมฆะ SortAlgo :: insertionSort (int data, int lenD) ( int key = 0; int i = 0; for (int j = 1; j =0 && ข้อมูล[i]>คีย์)( data = data[i]; i = i-1; data=key; ) ) )

ผสานการเรียงลำดับ

รหัสซี++

ถือเป็นโมฆะ SortAlgo::mergeSort (int data, int lenD) ( if (lenD>1)( int middle = lenD/2; int rem = lenD-middle; int * L = int ใหม่; int * R = int ใหม่; สำหรับ ( int i=0;i

จัดเรียงอย่างรวดเร็ว

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

รหัสซี++

ถือเป็นโมฆะ SortAlgo::quickSort(int * data, int const len) ( int const lenD = len; int pivot = 0; int ind = lenD/2; int i,j = 0,k = 0; if (lenD>1) ( int * L = int ใหม่ ; int * R = int ใหม่ ; pivot = data; สำหรับ (i=0;i

เป้าหมาย: ศึกษาอัลกอริธึมการเรียงลำดับอย่างรวดเร็วและการแก้ไข

ในบทนี้ เราจะศึกษาอัลกอริทึม Quicksort ซึ่งอาจมีการใช้บ่อยกว่าวิธีอื่นๆ พื้นฐานของอัลกอริธึมได้รับการพัฒนาในปี 1960 (C.A.R.Hoare) และได้รับการศึกษาอย่างรอบคอบโดยผู้คนจำนวนมากตั้งแต่นั้นมา Quicksort ได้รับความนิยมเป็นพิเศษเนื่องจากใช้งานง่าย เป็นอัลกอริธึมสำหรับวัตถุประสงค์ทั่วไปที่ค่อนข้างดีซึ่งทำงานได้ดีในหลาย ๆ สถานการณ์ในขณะที่ใช้ทรัพยากรน้อยกว่าอัลกอริธึมอื่น ๆ

ข้อดีหลักของอัลกอริธึมนี้คือ เป็นแบบ point-wise (ใช้สแต็กเพิ่มเติมเพียงเล็กน้อยเท่านั้น) ต้องการโดยเฉลี่ยเพียงการดำเนินการ N log N เพื่อเรียงลำดับองค์ประกอบ N และมีวงในที่สั้นมาก ข้อเสียของอัลกอริธึมคือเป็นแบบเรียกซ้ำ (การใช้งานจะยากมากเมื่อไม่มีการเรียกซ้ำ) ในกรณีที่แย่ที่สุดต้องใช้การดำเนินการ N2 นอกจากนี้ ยัง "เปราะบาง" มาก: มีข้อผิดพลาดเล็กน้อยในการใช้งาน ซึ่งสามารถ มองข้ามได้ง่าย อาจทำให้อัลกอริธึมทำงานได้แย่มากในบางไฟล์

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

การปรับปรุงอัลกอริธึมการเรียงลำดับอย่างรวดเร็วถือเป็นสิ่งล่อใจครั้งใหญ่: อัลกอริธึมการเรียงลำดับที่เร็วขึ้นนั้นเป็น "กับดักหนู" สำหรับโปรแกรมเมอร์ เกือบจะตั้งแต่ตอนที่ Oiaa ตีพิมพ์อัลกอริทึมของเขาเป็นครั้งแรก อัลกอริธึมเวอร์ชัน "ปรับปรุง" ก็เริ่มปรากฏในวรรณกรรม มีการทดลองและวิเคราะห์แนวคิดต่างๆ มากมาย แต่ก็ยังง่ายมากที่จะถูกหลอก เนื่องจากอัลกอริธึมมีความสมดุลที่ดีจนการปรับปรุงในส่วนใดส่วนหนึ่งอาจส่งผลให้ส่วนอื่นเสื่อมลงมากขึ้น เราจะศึกษารายละเอียดการปรับเปลี่ยนอัลกอริทึมนี้สามประการซึ่งให้การปรับปรุงที่สำคัญ

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

สาระสำคัญของอัลกอริทึม: จำนวนการดำเนินการในการเปลี่ยนตำแหน่งขององค์ประกอบภายในอาร์เรย์จะลดลงอย่างมากหากองค์ประกอบที่อยู่ห่างจากกันถูกสลับกัน เมื่อต้องการทำสิ่งนี้ ให้เลือกหนึ่งองค์ประกอบ x เพื่อเปรียบเทียบ พบองค์ประกอบแรกทางด้านซ้ายซึ่งไม่น้อยกว่า x และพบองค์ประกอบแรกทางด้านขวาซึ่งไม่เกิน x องค์ประกอบที่พบจะถูกสลับ หลังจากผ่านครั้งแรก องค์ประกอบทั้งหมดที่น้อยกว่า x จะอยู่ทางด้านซ้ายของ x และองค์ประกอบทั้งหมดที่มากกว่า x จะอยู่ทางด้านขวาของ x ทำเช่นเดียวกันกับสองครึ่งหนึ่งของอาร์เรย์ แบ่งครึ่งเหล่านี้ต่อไปจนกว่าจะมี 1 องค์ประกอบอยู่ในนั้น

โปรแกรม Quitsort;<=v) or (j=i+1); repeat inc(i) until (a[i]>ใช้ crt;

60,79, 82, 58, 39, 9, 54, 92, 44, 32 60,79, 82, 58, 39, 9, 54, 92, 44, 32 9,79, 82, 58, 39, 60, 54, 92, 44, 32 9,79, 82, 58, 39, 60, 54, 92, 44, 32 9, 32, 82, 58, 39, 60, 54, 92, 44, 79 9, 32, 44, 58, 39, 60, 54, 92, 82, 79 9, 32, 44, 58, 39, 54, 60, 92, 82, 79 9, 32, 44, 58, 39, 92, 60, 54, 82, 79 9, 32, 44, 58, 39, 54, 60, 79, 82, 92 9, 32, 44, 58, 54, 39, 60, 79, 82, 92 9, 32, 44, 58, 60, 39, 54, 79, 82, 92 9, 32, 44, 58, 54, 39, 60, 79, 82, 92 9, 32, 44, 58, 54, 39, 60, 79, 82, 92 9, 32, 44, 58, 54, 39, 60, 79, 82, 92 9, 32, 39, 58, 54, 44, 60, 79, 82, 92 9, 32, 39, 58, 54, 44, 60, 79, 82, 92 9, 32, 39, 44, 54, 58, 60, 79, 82, 92 9, 32, 39, 44, 58, 54, 60, 79, 82, 92 9, 32, 39, 44, 54, 58, 60, 79, 82, 92 9, 32, 39, 44, 54, 58, 60, 79, 92, 82 9, 32, 39, 44, 54, 58, 60, 79, 82, 92

ค่าคงที่ N=10;

พิมพ์ Mas=อาร์เรย์ของจำนวนเต็ม; var a: mas;

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

ลักษณะการทำงานของการเรียงลำดับแบบรวดเร็ว

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

CN = 2CN/2+N - กรณีที่ดีที่สุด

(2CN/2 ครอบคลุมค่าใช้จ่ายในการจัดเรียงไฟล์ย่อยผลลัพธ์ทั้งสองไฟล์ N คือต้นทุนในการประมวลผลแต่ละองค์ประกอบโดยใช้ตัวชี้ตัวใดตัวหนึ่งหรือตัวชี้อื่น) เรายังรู้ด้วยว่าค่าโดยประมาณของนิพจน์นี้คือ CN = N lg N

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

คุณสมบัติ 1 Quicksort ใช้การเปรียบเทียบ 2N ln N โดยเฉลี่ย

วิธีการปรับปรุงการเรียงลำดับอย่างรวดเร็ว

1. ไฟล์ย่อยขนาดเล็ก

การปรับปรุงครั้งแรกในอัลกอริธึมการเรียงลำดับอย่างรวดเร็วนั้นมาจากการสังเกตว่าโปรแกรมรับประกันว่าจะเรียกตัวเองในไฟล์ย่อยขนาดเล็กจำนวนมาก ดังนั้นเราควรใช้วิธีการเรียงลำดับที่ดีที่สุดเมื่อเราพบไฟล์ย่อยขนาดเล็ก วิธีที่ชัดเจนในการบรรลุเป้าหมายนี้คือการเปลี่ยนการตรวจสอบที่จุดเริ่มต้นของฟังก์ชันเรียกซ้ำจาก "if r>l then" เป็นการเรียกการเรียงลำดับการแทรก (แก้ไขอย่างเหมาะสมเพื่อให้เคารพขอบเขตของไฟล์ย่อยที่ถูกเรียงลำดับ): "if r-l<=M then insertion(l, r)." Значение для M не обязано быть "самым-самым" лучшим: алгоритм работает примерно одинаково для M от 5 до 25. Время работы программы при этом снижается примерно на 20% для большинства программ.

สำหรับไฟล์ย่อยขนาดเล็ก (5-25 องค์ประกอบ) การเรียงลำดับอย่างรวดเร็วจะเรียกตัวเองหลายครั้ง (ในตัวอย่างของเรา สำหรับ 10 องค์ประกอบจะเรียกตัวเองว่า 15 ครั้ง) ดังนั้นคุณไม่ควรใช้การเรียงลำดับอย่างรวดเร็ว แต่เป็นการเรียงลำดับการแทรก

ขั้นตอน QuickSort (l,t:จำนวนเต็ม); var i: จำนวนเต็ม; เริ่มต้นถ้า t-l>m จากนั้นเริ่มต้น i:=part(l,t);

QuickSort(l,i-1);

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

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

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

การแลกเปลี่ยนขั้นตอน (i, j: จำนวนเต็ม); var k:จำนวนเต็ม; เริ่มต้น k:=a[i];

ก[i]:=ก[เจ];

ก[เจ]:=k; จบ; ขั้นตอน Mediana; var i: จำนวนเต็ม; เริ่มต้น i:=n div 4;(รูปที่) ถ้า a[i]>a แล้วถ้า a[i]>a แล้วแลกเปลี่ยน(i,n) อื่น ๆ แลกเปลี่ยน(i*3,n) อื่นถ้า a>a แล้วแลกเปลี่ยน (i*2,n);

เรียงลำดับอย่างรวดเร็ว(1,n); จบ;

3. การใช้งานแบบไม่เรียกซ้ำ

คุณสามารถเขียนอัลกอริทึมนี้ใหม่ได้โดยไม่ต้องใช้การเรียกซ้ำโดยใช้สแต็ก แต่เราจะไม่ทำอย่างนั้นที่นี่

การรวมการใช้งานการแบ่งค่ามัธยฐานสามแบบแบบไม่เรียกซ้ำเข้ากับการตัดออกเป็นไฟล์ขนาดเล็กสามารถปรับปรุงเวลาการทำงานของอัลกอริทึมได้ 25% ถึง 30%

ดังนั้น ในบทเรียนวันนี้ เราจึงดูที่อัลกอริธึมการเรียงลำดับแบบด่วน

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

ความสามารถพื้นฐานในการจัดเรียงไฟล์อย่างมีประสิทธิภาพโดยการทำงานกับส่วนต่างๆ ของไฟล์และไม่เกินขอบเขตของส่วนนั้นได้มาจากอัลกอริธึมการรวม

การรวมหมายถึงการรวมลำดับที่เรียงลำดับสองรายการ (หรือมากกว่า) ให้เป็นลำดับเดียวโดยการวนรอบองค์ประกอบที่มีอยู่ในปัจจุบัน

การรวมเป็นการดำเนินการที่ง่ายกว่าการเรียงลำดับมาก

เราจะดูอัลกอริธึมการรวม 2 แบบ:

การควบรวมกิจการโดยตรง อัลกอริธึมของโบส-เนลสัน

ลำดับ a แบ่งออกเป็นสองซีก b และ c

ลำดับ b และ c ถูกรวมเข้าด้วยกันโดยการรวมแต่ละองค์ประกอบเข้าเป็นคู่ที่เรียงลำดับ

ลำดับผลลัพธ์จะได้รับชื่อ a หลังจากนั้นจึงทำซ้ำขั้นตอนที่ 1 และ 2 ในกรณีนี้ คู่ที่ได้รับคำสั่งจะรวมกันเป็นสี่เท่าที่ได้รับคำสั่ง

ขั้นตอนก่อนหน้านี้จะถูกทำซ้ำ โดยสี่จะรวมกันเป็นแปด ฯลฯ จนกระทั่งลำดับทั้งหมดถูกเรียงลำดับ เพราะ ความยาวของลำดับสองเท่าในแต่ละครั้ง

ตัวอย่าง

ลำดับดั้งเดิม

ก = 44 55 12 42 94 18 06 67 1 b = 44 55 12 42 วิ = 94 18 06 67 ก = 44 94" 18 55" 06 12" 42 67 2 b = 44 94" 18 55" วิ = 06 12" 42 67 ก = 06 12 44 94" 18 42 55 67" 3 ข = 06 12 44 94" ค = 18 42 55 67" ก = 06 12 18 42 44 55 67 94

การดำเนินการที่ประมวลผลชุดข้อมูลทั้งหมดเพียงครั้งเดียวเรียกว่าเฟส

กระบวนการย่อยที่เล็กที่สุดซึ่งเมื่อทำซ้ำแล้วจะทำให้เกิดกระบวนการเรียงลำดับเรียกว่าผ่านหรือขั้นตอน

ในตัวอย่างของเรา การเรียงลำดับจะดำเนินการในสามรอบ แต่ละรอบประกอบด้วยเฟสแยกและเฟสรวม

ข้อเสียเปรียบหลักของการเรียงลำดับแบบผสานคือขนาดหน่วยความจำที่เดิมครอบครองโดยข้อมูลที่เรียงลำดับเป็นสองเท่า ลองพิจารณาอัลกอริธึมการรวมแบบเรียกซ้ำที่เสนอโดย Bose และ Nelson ซึ่งไม่ต้องการหน่วยความจำสำรอง

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

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

ค่าคอนสต n=200; พิมพ์ tipkl=word; tip = บันทึก kl: tipkl; z:อาร์เรย์ของการสิ้นสุดจริง; Var A: อาร์เรย์ของปลาย; เจ:คำ; ขั้นตอน Bose (Var AA; voz:Boolean); วาร์ ม,เจ:คำ; x:ทิป; (ทิป - ประเภทของเรคคอร์ดที่กำลังจัดเรียง) A: อาร์เรย์ของทิป Absolute AA; ขั้นตอน Sli(j,r,m: word); (r คือระยะห่างระหว่างจุดเริ่มต้นของส่วนที่ผสาน และ m คือขนาดของส่วนนั้น j คือหมายเลขบันทึกที่เล็กที่สุด) เริ่มต้นถ้า j+r<=n Then If m=1 Then Begin If voz Xor (A[j].kl < A.kl) Then Begin x:=A[j]; A[j]:= A; A:=x End End Else Begin m:=m div 2; Sli(j,r,m); {Слияние "начал"} If j+r+m<=n Then Sli(j+m,r,m); {Слияние "концов"} Sli(j+m,r-m,m) End {Слияние в центральной части} End{блока Sli}; Begin m:=1; Repeat j:=1; {Цикл слияния списков равного размера: } While j+m<=n do Begin Sli(j,m,m); j:=j+m+m End; m:=m+m {Удвоение размера списка перед началом нового прохода} Until m >= n (จุดสิ้นสุดของลูปที่ใช้แผนผังผสานทั้งหมด) สิ้นสุด(บล็อก Bose); เริ่มต้น สุ่ม; สำหรับ j:=1 ถึง n ให้เริ่มต้น A[j].kl:= Random(65535); เขียน(A[j].kl:8); จบ; อ่าน; โบส(ก,จริง); สำหรับ j:=1 ถึง n ทำ Write(A[j].kl:8); อ่านจบแล้ว

ฟิวชั่นธรรมชาติ (นอยมันน์)

มันรวมชิ้นส่วนที่ได้รับคำสั่งซึ่งเกิดขึ้นเองตามธรรมชาติในอาเรย์ดั้งเดิม นอกจากนี้ยังอาจเป็นผลมาจากการประมวลผลข้อมูลก่อนหน้านี้ ไม่จำเป็นต้องนับชิ้นส่วนที่รวมขนาดเท่ากัน

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

ตัวอย่าง:

ให้คีย์บันทึกได้รับ

5 7 8 3 9 4 1 7 6

กำลังมองหารายการย่อย

รายการย่อยที่ 1, 3, 5 ฯลฯ จะรวมกันเป็นรายการทั่วไปรายการเดียว และรายการย่อยที่ 2, 4 ฯลฯ จะรวมอยู่ในรายการอื่น

ลองรวม 1 รายการย่อยจาก 1 รายการกับ 1 รายการย่อยจาก 2 รายการ, 2 รายการย่อยจาก 1 รายการ และ 2 รายการย่อยจาก 2 รายการ เป็นต้น

จะได้รับโซ่ต่อไปนี้

3 --> 5 --> 7 --> 8 --> 9 และ 1 --> 4 --> 7

รายการย่อยที่ประกอบด้วยรายการ "6" ไม่มีคู่และถูกรวม "บังคับ" กับเชนสุดท้ายซึ่งอยู่ในรูปแบบ 1 --> 4--> 6 --> 7

ด้วยจำนวนผู้เข้าร่วมที่น้อยของเรา ด่านที่ 2 ซึ่งทั้งสองเชนมารวมกันจะเป็นขั้นตอนสุดท้าย

โดยทั่วไป ในแต่ละขั้นตอนของรายการย่อย ผลลัพธ์ของการรวมรายการย่อยเริ่มต้น 1 และ 2 ของรายการจะกลายเป็นจุดเริ่มต้นของรายการที่ 1 ใหม่ และผลลัพธ์ของการรวมสองรายการย่อยถัดไปจะกลายเป็นจุดเริ่มต้นของรายการที่ 2 รายการย่อยต่อไปนี้ที่เกิดขึ้นจะสลับกันรวมอยู่ในรายการที่ 1 และ 2

สำหรับการใช้งานซอฟต์แวร์ อาร์เรย์ sp จะถูกสร้างขึ้น: องค์ประกอบ sp[i] คือหมายเลขของบันทึกที่ตามหลัง i-th

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

ทำซ้ำ (การทำซ้ำของการรวมรายการย่อย) ถ้า A[j].kl< A[i].kl Then {Выбирается меньшая запись} Begin sp[k]:=j; k:=j; j:=sp[j]; If j<=0 Then {Сцепление с остатком "i"-подсписка} Begin sp[k]:=i; Repeat m:=i; i:=sp[i] Until i<=0 End End Else Begin sp[k]:=i; k:=i; i:=sp[i]; If i<=0 Then {Сцепление с остатком "j"-подсписка} Begin sp[k]:=j; Repeat m:=j; j:=sp[j] Until j<=0 End End; If j<=0 Then Begin sp[m]:= 0; sp[p]:=-sp[p]; i:=-i; j:=-j; If j<>0 จากนั้น p:=r; เค:=ร; r:=m สิ้นสุดจนกระทั่ง j=0;

(การอ้างอิงที่เป็นค่าว่าง (sp[m]:= 0) จะถูกเพิ่มต่อท้ายรายการย่อยที่สร้างขึ้นเสมอ เนื่องจากอาจเป็นรายการสุดท้าย

การดำเนินการ sp[p]:= -sp[p] หมายถึงจุดสิ้นสุดของรายการย่อยที่สร้างขึ้นก่อนหน้านี้ด้วยเครื่องหมายลบ

ดังนั้น ในบทเรียนวันนี้ เราจึงดูที่การรวมอัลกอริธึม


รหัสเทียม
QuickSort(array a, upper bound N) ( เลือกองค์ประกอบ pivot p - ตรงกลางของอาร์เรย์ แยกอาร์เรย์ที่องค์ประกอบนั้น หากอาร์เรย์ย่อยทางด้านซ้ายของ p มีมากกว่าหนึ่งองค์ประกอบ ให้เรียก QuickSort ไว้ หากอาร์เรย์ย่อยทางด้านขวาของ p มีมากกว่าหนึ่งองค์ประกอบ เรียก QuickSort ให้เขา ) การใช้งานใน C
แม่แบบ เป็นโมฆะ QuickSortR(T* a, N ยาว) ( // อินพุตคืออาร์เรย์ a, a[N] เป็นองค์ประกอบสุดท้ายยาว i = 0, j = N-1; //นำพอยน์เตอร์ไปยังตำแหน่งเดิม T อุณหภูมิ, p; พี = ก[ N>>1 ]; // องค์ประกอบส่วนกลาง// ขั้นตอนการแบ่ง< p) i++; while (a[j] >ทำ ( ในขณะที่ (a[i]<= j) { temp = a[i]; a[i] = a[j]; a[j] = temp; i++; j--; } } while (i<=j); พี)เจ--;ถ้า (i

// การโทรซ้ำหากมีสิ่งใดที่ต้องเรียงลำดับ

ถ้า (j > 0) QuickSortR(a, j);

ถ้า (N > i) QuickSortR(a+i, N-i); -

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

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

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

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

    การปรับเปลี่ยนรหัสและวิธีการ

    เนื่องจากการเรียกซ้ำและค่าใช้จ่ายอื่นๆ Quicksort อาจไม่เร็วขนาดนั้นสำหรับอาร์เรย์แบบสั้น ดังนั้น หากมีองค์ประกอบ CUTOFF น้อยลงในอาเรย์ (ค่าคงที่ขึ้นอยู่กับการใช้งาน โดยปกติจะอยู่ระหว่าง 3 ถึง 40) การเรียงลำดับการแทรกจะถูกเรียก เพิ่มความเร็วได้ถึง 15%<=CUTOFF элементов, отсортированные друг относительно друга. Близкие элементы имеют близкие позиции, поэтому, аналогично сортировке Шелла, вызывается insertSort(), которая доводит процесс до конца.

    หากต้องการนำวิธีนี้ไปใช้ คุณสามารถแก้ไขฟังก์ชัน QuickSortR ได้โดยการแทนที่ 2 บรรทัดสุดท้ายด้วย ถ้า (j > CUTOFF) QuickSortR(a, j); ถ้า (N > i + CUTOFF) QuickSortR(a+i, N-i);}

  1. ในกรณีของการเรียกซ้ำอย่างชัดเจน เช่นเดียวกับในโปรแกรมด้านบน ไม่เพียงแต่ขอบเขตของอาร์เรย์ย่อยจะถูกจัดเก็บบนสแต็ก แต่ยังมีพารามิเตอร์ที่ไม่จำเป็นจำนวนหนึ่งด้วย เช่น ตัวแปรท้องถิ่น หากคุณจำลองสแต็กโดยทางโปรแกรม ขนาดของสแต็กสามารถลดลงได้หลายครั้ง
  2. ยิ่งอาร์เรย์ถูกแบ่งส่วนที่เท่ากันมากเท่าไรก็ยิ่งดีเท่านั้น ดังนั้นจึงขอแนะนำให้ใช้ค่าเฉลี่ยของสามเป็นข้อมูลอ้างอิงและหากอาร์เรย์มีขนาดใหญ่เพียงพอแล้วจะมีองค์ประกอบเก้าองค์ประกอบโดยพลการ
  3. ปล่อยให้ลำดับอินพุตแย่มากสำหรับอัลกอริทึม ตัวอย่างเช่นได้รับการคัดเลือกเป็นพิเศษเพื่อให้องค์ประกอบตรงกลางกลายเป็นขั้นต่ำทุกครั้ง จะทำให้ QuickSort ทนต่อ “การก่อวินาศกรรม” ดังกล่าวได้อย่างไร? ง่ายมากที่จะเลือกองค์ประกอบสุ่มของอาร์เรย์อินพุตเป็นข้อมูลอ้างอิง จากนั้นรูปแบบที่ไม่พึงประสงค์ใดๆ ในสตรีมอินพุตจะถูกทำให้เป็นกลาง อีกทางเลือกหนึ่งคือการสุ่มจัดเรียงองค์ประกอบอาเรย์ใหม่ก่อนที่จะเรียงลำดับ
  4. การเรียงลำดับด่วนยังสามารถใช้กับรายการที่เชื่อมโยงแบบทวีคูณได้ ปัญหาเดียวของเรื่องนี้คือการไม่สามารถเข้าถึงองค์ประกอบสุ่มได้โดยตรง ดังนั้นคุณต้องเลือกองค์ประกอบแรกเป็นข้อมูลอ้างอิง และหวังว่าจะได้ข้อมูลเริ่มต้นที่ดี หรือสุ่มจัดเรียงองค์ประกอบใหม่ก่อนที่จะเรียงลำดับ

ลองพิจารณากรณีที่เลวร้ายที่สุด เมื่อองค์ประกอบสนับสนุนที่เลือกแบบสุ่มกลายเป็นสิ่งที่แย่มาก (เกือบจะสุดขั้ว) ความน่าจะเป็นของสิ่งนี้ต่ำมาก โดยที่ n = 1,024 นั้นน้อยกว่า 2 -50 ดังนั้นความสนใจจึงเป็นทฤษฎีมากกว่าภาคปฏิบัติ อย่างไรก็ตาม พฤติกรรม "การเรียงลำดับอย่างรวดเร็ว" ถือเป็น "เกณฑ์มาตรฐาน" สำหรับอัลกอริธึมการแบ่งแยกและพิชิตที่มีการใช้งานคล้ายกัน ไม่ใช่ทุกที่ที่เป็นไปได้ที่จะลดความน่าจะเป็นของกรณีที่เลวร้ายที่สุดให้เหลือเกือบศูนย์ ดังนั้นสถานการณ์นี้จึงสมควรได้รับการศึกษา

เพื่อความแน่นอน ให้เลือกองค์ประกอบที่เล็กที่สุดต่อนาทีในแต่ละครั้ง จากนั้นขั้นตอนการแยกจะย้ายองค์ประกอบนี้ไปยังจุดเริ่มต้นของอาร์เรย์ และสองส่วนจะถูกส่งไปยังระดับถัดไปของการเรียกซ้ำ: ส่วนหนึ่งจากองค์ประกอบเดียว a min อีกส่วนหนึ่งประกอบด้วยองค์ประกอบ n-1 ที่เหลือของอาร์เรย์ จากนั้นกระบวนการจะทำซ้ำสำหรับส่วนหนึ่งขององค์ประกอบ (n-1).. และอื่นๆ..
การใช้โค้ดแบบเรียกซ้ำเหมือนกับที่กล่าวมาข้างต้น หมายความว่าไม่มีการเรียกแบบเรียกซ้ำแบบซ้อนไปยัง QuickSort
การโทรซ้ำแต่ละครั้งหมายถึงการจัดเก็บข้อมูลเกี่ยวกับสถานะปัจจุบัน ดังนั้นการเรียงลำดับจึงต้องใช้หน่วยความจำเพิ่มเติม O(n)... และไม่ใช่แค่ที่ใดก็ได้ แต่บนสแต็ก หาก n มีขนาดใหญ่เพียงพอ ข้อกำหนดดังกล่าวอาจนำไปสู่ผลลัพธ์ที่คาดเดาไม่ได้

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

รหัสเทียม
Iterative QuickSort (อาร์เรย์ a, ขนาดขนาด) (พุชคำขอเพื่อเรียงลำดับอาร์เรย์จาก 0 ถึงขนาด -1 บนสแต็ก do (ป๊อปขอบเขต lb และ ub ของอาร์เรย์ปัจจุบันจากสแต็ก do ( 1. ดำเนินการแยก การดำเนินการกับอาร์เรย์ปัจจุบัน a. 2. ส่งขอบเขตของส่วนที่ใหญ่กว่าของผลลัพธ์ไปยังสแต็ก 3. ย้ายขอบเขตของ ub, lb เพื่อให้ชี้ไปที่ส่วนที่เล็กกว่า) ในขณะที่ส่วนที่เล็กกว่าประกอบด้วยสองส่วนขึ้นไป องค์ประกอบ) ในขณะที่มีการร้องขอบนสแต็ก) การใช้งานใน C.
#กำหนด MAXSTACK 2048 // ขนาดสแต็กสูงสุดแม่แบบ เป็นโมฆะ qSortI (T a ขนาดยาว) ( ยาว i, j; // พอยน์เตอร์ที่เกี่ยวข้องกับการแบ่งปอนด์ยาว, ub; // ขอบเขตของแฟรกเมนต์ที่ถูกจัดเรียงในลูปปอนด์ยาว, กอง; // ขอสแต็ก // แต่ละคำขอจะถูกระบุด้วยค่าคู่หนึ่ง // คือ: ซ้าย (lbstack) และขวา (ubstack) // ขอบเขตของช่วงเวลาสแต็คโพสยาว = 1; // ตำแหน่งสแต็กปัจจุบันโพสยาว; // ตรงกลางของอาร์เรย์ทีเดือย; // องค์ประกอบสนับสนุนอุณหภูมิ; ปอนด์สแต็ค = 0; ubstack = ขนาด -1; ทำ (// นำขอบเขต lb และ ub ของอาร์เรย์ปัจจุบันออกจากสแต็ก< pivot) i++; while (pivot < a[j]) j--; if (i <= j) { temp = a[i]; a[i] = a[j]; a[j] = temp; i++; j--; } } while (i <= j); ปอนด์ = lbstack[ สแต็คโพส ]; ub = ubstack[ สแต็คโพส ]; สแต็คโพส--; ทำ (// ขั้นตอนที่ 1 แยกตามองค์ประกอบเดือย< ppos) { ppos = (ปอนด์ + ub) >> 1;// ขั้นตอนที่ 1 แยกตามองค์ประกอบเดือย< ub) { ฉัน = ปอนด์; เจ = อูบ; เดือย = ก;ทำ ( ในขณะที่ (a[i] // ตอนนี้ตัวชี้ i ชี้ไปที่จุดเริ่มต้นของอาร์เรย์ย่อยด้านขวา// j - ไปทางซ้าย (ดูภาพประกอบด้านบน), lb ? เจ? ฉัน? ub // เป็นไปได้ว่าตัวชี้ i หรือ j อยู่นอกขอบเขตอาร์เรย์ // ขั้นตอนที่ 2, 3 ดันส่วนใหญ่ลงบนสแต็กแล้วย้าย lb,ubถ้า (i // ด้านขวามีขนาดใหญ่กว่า// ถ้ามีมากกว่า 1 องค์ประกอบก็จำเป็น< ub); สแต็คโพส++;// เรียงลำดับขอสแต็ก ปอนด์สแต็ค [ สแต็คโพส ] = ฉัน;}

ขนาดสแต็กที่มีการใช้งานนี้จะอยู่ในลำดับ O(log n) เสมอ ดังนั้นค่าที่ระบุใน MAXSTACK จึงเกินพอ

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

ฉันจะไม่ใช่คนแรกและจะไม่ใช่คนสุดท้ายที่บอกว่า "เร็ว" เป็นเพียงชื่อเท่านั้นและตอนนี้มีอะนาล็อกมากมายและโดยทั่วไปแล้วข้อมูลแต่ละประเภทต้องมีการเรียงลำดับของตัวเอง ใช่ นี่เป็นเรื่องจริงทั้งหมด แต่ความจริงนี้ไม่ได้เปลี่ยนข้อเท็จจริงง่ายๆ อย่างนั้น การใช้การเรียงลำดับอย่างรวดเร็วของคุณเองจะทำให้ทักษะการเขียนโปรแกรมของคุณดีขึ้นโดยทั่วไป และมันจะเป็นหลักสูตรของมหาวิทยาลัยตลอดไป ฉันมั่นใจ ด้วยเหตุผลเดียวกัน จึงเลือกภาษาการเขียนโปรแกรมสำหรับการใช้งาน เนื่องจากคุณสามารถฝึกใช้พอยน์เตอร์ใน C/C++ ได้ทันที

ฉันเสนอให้ลงมือทำธุรกิจและพิจารณาสาระสำคัญของอัลกอริทึมโดยสังเขปก่อน

การเรียงลำดับอย่างรวดเร็วทำงานอย่างไร

แผนภาพอัลกอริทึมสามารถอธิบายได้ดังต่อไปนี้:

  1. เลือก สนับสนุน องค์ประกอบในอาร์เรย์ - มักพบตัวแปรที่มีองค์ประกอบส่วนกลาง
  2. แบ่งอาร์เรย์ออกเป็น สองส่วน ดังนี้ องค์ประกอบทั้งหมดจาก ซ้าย ส่วนนั้น มากกว่าหรือเท่ากับ สนับสนุนโยนเข้าไป ขวา ในทำนองเดียวกันองค์ประกอบทั้งหมดจาก ขวา , ที่ น้อยกว่าหรือเท่ากับ เราโยนมันไปที่แนวรับ ซ้าย ส่วนหนึ่ง.
  3. จากขั้นตอนก่อนหน้านี้ องค์ประกอบที่น้อยกว่าหรือเท่ากับองค์ประกอบตรงกลางจะยังคงอยู่ทางด้านซ้ายของอาร์เรย์ และองค์ประกอบที่มากกว่าหรือเท่ากับทางด้านขวาจะยังคงอยู่
    ซึ่งสามารถแสดงให้เห็นได้อย่างชัดเจนดังนี้:
    |———————|—————————|———————|
    - มาส[i]<= mid | mid = mas | mas[i] >= กลาง |
    |———————-|—————————|———————|
  4. เราทำซ้ำการกระทำแบบวนซ้ำสำหรับส่วนซ้ายและขวาของอาร์เรย์

การเข้าสู่การเรียกซ้ำจะหยุดทันทีที่ขนาดของทั้งสองส่วนน้อยกว่าหรือเท่ากับหนึ่ง

ฉันยืมภาพประกอบขั้นตอนหนึ่งของอัลกอริทึมมา มันชัดเจนอย่างเจ็บปวด

การใช้งาน Quicksort แบบเรียกซ้ำ

ฟังก์ชันนี้ใช้เป็นอินพุตของอาร์เรย์เอง (ตัวชี้ไปยังจุดเริ่มต้น) และขนาดของอาร์เรย์

Void qsortRecursive(int *mas, int size) ( //ตัวชี้ไปยังจุดเริ่มต้นและจุดสิ้นสุดของอาร์เรย์ int i = 0; int j = ขนาด - 1; //องค์ประกอบกลางของอาร์เรย์ int mid = mas; //หาร array do ( // เราผ่านองค์ประกอบต่างๆ โดยมองหาองค์ประกอบที่ต้องถ่ายโอนไปยังส่วนอื่น // ทางด้านซ้ายของอาร์เรย์เราข้าม (ปล่อยไว้) องค์ประกอบที่เล็กกว่าองค์ประกอบส่วนกลาง ในขณะที่(mas[ ฉัน]< mid) { i++; } //В правой части пропускаем элементы, которые больше центрального while(mas[j] >กลาง) ( j--; ) // สลับองค์ประกอบถ้า (i<= j) { int tmp = mas[i]; mas[i] = mas[j]; mas[j] = tmp; i++; j--; } } while (i <= j); //Рекурсивные вызовы, если осталось, что сортировать if(j >0) ( // "ชิ้นซ้าย" qsortRecursive (mas, j + 1); ) ถ้า (i< size) { //"Првый кусок" qsortRecursive(&mas[i], size - i); } }

บทสรุป

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

อัปเดต: 18/03/2019

จัดเรียงอย่างรวดเร็ว (จัดเรียงอย่างรวดเร็ว) หรือการเรียงลำดับ Hoare เป็นหนึ่งในอัลกอริธึมการเรียงลำดับข้อมูลที่เร็วที่สุด

อัลกอริธึมของ Hoare เป็นเวอร์ชันแก้ไขของวิธีการแลกเปลี่ยนโดยตรง รูปแบบอื่นที่เป็นที่นิยมของวิธีนี้คือ การเรียงลำดับฟองและ การเรียงลำดับเครื่องปั่นต่างจาก Quicksort ตรงที่ไม่มีประสิทธิภาพมากนัก

อัลกอริธึมการเรียงลำดับอย่างรวดเร็วทำงานอย่างไร

แนวคิดของอัลกอริทึมมีดังนี้:

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

เนื่องจากวิธีการ Quicksort แบ่งอาร์เรย์ออกเป็นส่วนๆ จึงอยู่ในกลุ่มของอัลกอริทึม แบ่งแยกและพิชิต.

การใช้การเรียงลำดับอย่างรวดเร็ว

ใช้ระบบ; class Program ( //วิธีการแลกเปลี่ยนองค์ประกอบอาร์เรย์ static void Swap(ref int x, ref int y) ( var t = x; x = y; y = t; ) //วิธีการส่งคืนดัชนีขององค์ประกอบอ้างอิง static int Partition (int array , int minIndex, int maxIndex) ( var pivot = minIndex - 1; สำหรับ (var i = minIndex; i< maxIndex; i++) { if (array[i] < array) { pivot++; Swap(ref array, ref array[i]); } } pivot++; Swap(ref array, ref array); return pivot; } //быстрая сортировка static int QuickSort(int array, int minIndex, int maxIndex) { if (minIndex >= maxIndex) ( return array; ) var pivotIndex = Partition(array, minIndex, maxIndex);< a.Length; ++i) { Console.Write("a[{0}] = ", i); a[i] = Convert.ToInt32(Console.ReadLine()); } Console.WriteLine("Упорядоченный массив: {0}", string.Join(", ", QuickSort(a))); Console.ReadLine(); } }

QuickSort (อาร์เรย์, minIndex, pivotIndex - 1);