การแนะนำ
ความซับซ้อนของ Mergesort สำหรับอินพุตที่ถูกเรียงลำดับในสิ่งที่ตรงกันข้ามคือ O (N^2) และ Timsort นั้นถูกสร้างขึ้นโดยการเพิ่มประสิทธิภาพ Mergesort สำหรับสถานการณ์นี้ ความซับซ้อนเฉลี่ยคือ n*o (log n) กรณีที่ดีที่สุดคือ o (n) และกรณีที่เลวร้ายที่สุดคือ n*o (log n) และ Timsort เป็นความมั่นคง แนวคิดคือการแบ่งคอลัมน์เรียงลำดับก่อนจากนั้นรวมพาร์ติชันซึ่งดูเหมือนขั้นตอน Mergesort แต่มีการปรับให้เหมาะสมสำหรับข้อมูลย้อนกลับและขนาดใหญ่
แนวคิดการเพิ่มประสิทธิภาพของการเรียงลำดับการเรียงลำดับ
มีวิธีการเพิ่มประสิทธิภาพหลายวิธีสำหรับการเรียงลำดับการรวม:
เช่นเดียวกับการเรียงลำดับอย่างรวดเร็วคุณสามารถใช้การเรียงลำดับแทรกหรือเลือกการเรียงลำดับสำหรับอาร์เรย์ขนาดเล็กเพื่อหลีกเลี่ยงการโทรแบบเรียกซ้ำ
ก่อนที่จะเรียกว่า Merge () คุณสามารถตรวจสอบได้ว่า [กลาง] น้อยกว่าหรือเท่ากับ [Mid+1] ถ้าเป็นเช่นนั้นไม่จำเป็นต้องรวมกันอาร์เรย์จะถูกสั่งแล้ว เหตุผลนั้นง่ายมาก เนื่องจาก subarrays ทั้งสองได้รับการสั่งซื้อแล้ว [mid] เป็นค่าสูงสุดของ subarray แรกและ [mid+1] เป็นค่าต่ำสุดของ subarray ที่สอง เมื่อ [mid] <= a [mid+1] อาร์เรย์จะถูกสั่งโดยรวม
เพื่อประหยัดเวลาในการคัดลอกองค์ประกอบไปยังอาร์เรย์เสริมบทบาทของอาร์เรย์ดั้งเดิมและอาร์เรย์เสริมสามารถเปลี่ยนได้ในแต่ละระดับของการโทรแบบเรียกซ้ำ
กระบวนการผสานในวิธีการผสาน () จำเป็นต้องพิจารณาว่าฉันและเจข้ามขอบเขตนั่นคือครึ่งหนึ่งของขอบถูกใช้ไปหรือไม่ อีกวิธีหนึ่งในการลบรหัสที่ตรวจพบว่าครึ่งหนึ่งของด้านข้างหมดหรือไม่ ขั้นตอนที่เฉพาะเจาะจงคือการคัดลอกครึ่งหลังของอาร์เรย์ a [] ไปยัง aux [] ตามลำดับจากมากไปน้อยจากนั้นรวมจากปลายทั้งสอง สำหรับอาร์เรย์ {1,2,3} และ {2,3,5} subarray แรกถูกคัดลอกตามปกติและที่สองถูกคัดลอกจากด้านหลังไปด้านหน้าและองค์ประกอบสุดท้ายใน aux [] คือ {1,2,3,5,3,2} ข้อเสียของวิธีนี้คือมันทำให้การเรียงลำดับผสานกลายเป็นการเรียงลำดับที่ไม่เสถียร การใช้งานรหัสมีดังนี้:
โมฆะผสาน (int [] a, int lo, int กลาง, int hi, int [] aux) {สำหรับ (int k = lo; k <= mid; k ++) {aux [k] = a [k];} สำหรับ (int k = mid+1; k <= hi; k ++) {aux [k] = a [hi - k+1]; // จากทั้งสองปลายถึงกลางสำหรับ (int k = lo; k <= hi; k ++) ถ้า (aux [i] <= aux [j]) a [k] = aux [i ++]; อื่น ๆ [k] = aux [j--];}ขั้นตอนของ Timsort
พาร์ทิชัน
แนวคิดของการแบ่งพาร์ติชันคือการสแกนอาร์เรย์หนึ่งครั้งและใช้ลำดับเชิงบวกอย่างต่อเนื่อง (หากมีการจัดเรียงลำดับจากน้อยไปมากลำดับบวกจะเป็นลำดับจากน้อยไปหามาก) หรือ [อย่างเคร่งครัด] (เพื่อให้แน่ใจว่าความเสถียรของอัลกอริทึมการเรียงลำดับ) เป็นพาร์ติชัน (RUN) หากเป็นลำดับย้อนกลับให้ย้อนกลับองค์ประกอบในพาร์ติชัน ตัวอย่างเช่น
1, 2, 3, 6, 4, 5, 8, 6, 4 ผลการแบ่งพาร์ติชันคือ
[1,2,3,6], [4,5,8], [6,4]
จากนั้นกลับลำดับย้อนกลับ
[1,2,3,6], [4,5,8], [4,6]
ผสาน
พิจารณาตัวอย่างที่รุนแรงเช่นความยาวของพาร์ติชันคือ 10,000, 10, 1,000, 10, 10 และ 10 แน่นอนว่าเราหวังว่าจะรวม 10 10 เป็น 20, 20 และ 1,000 ใน 1020 ก่อน หากเรารวมจากซ้ายไปขวาเราจะใช้อาร์เรย์ 10,000 และการรวมกันของตัวเลขขนาดเล็กในแต่ละครั้งซึ่งแพงเกินไป ดังนั้นเราสามารถใช้กลยุทธ์เพื่อเพิ่มประสิทธิภาพลำดับของการผสาน
ตัวอย่าง
การใช้ compolabletimsort.sort () ใน Java เป็นตัวอย่างการเรียกใช้สแต็กใช้เพื่อตรวจสอบว่าควรรวมกันหรือไม่
ถ้า (nremaining <min_merge) {int initrunlen = countrunandmakeascending (a, lo, hi); BinarySort (A, LO, HI, LO + InitRunlen); กลับ; -เรียงลำดับน้อยกว่า min_merge (32) และเรียงลำดับโดยตรงด้วยการแทรกไบนารีหลังจากการแบ่งพาร์ติชัน
int minrun = minrunlength (nremaining); ทำ {// ค้นหาตำแหน่งเริ่มต้นของพาร์ติชันถัดไปและพลิกลำดับย้อนกลับ int runlen = countrunandmakeascending (a, lo, hi); // ตรวจสอบให้แน่ใจว่าการวิ่งในสแต็กวิ่งนั้นมากกว่า MinRun หากพาร์ติชันปัจจุบันมีขนาดเล็กเกินไปให้นำองค์ประกอบออกจากด้านหลังเพื่อแต่งหน้าถ้า (Runlen <Minrun) {int force = nremaining <= minrun? nremaining: minrun; BinarySort (a, lo, lo + force, lo + runlen); runlen = force; } // ใส่รันลงในสแต็ก ts.pushrun (lo, runlen); // ตัดสินว่าควรรวมกันหรือไม่ ฉันเริ่มต้นจากด้านบนของสแต็กและรู้ว่ามันไม่สามารถรวมกันได้ // 1 runlen [i - 3]> runlen [i - 2] + runlen [i - 1] // 2 runlen [i - 2]> runlen [i - 1] ts.mergecollapse (); lo += runlen; nremaining -= runlen; } ในขณะที่ (nremaining! = 0); // ผสานการรันที่เหลือทั้งหมดเพื่อทำการเรียงลำดับการเรียงลำดับ LO == HI; // ผสานการรันที่เหลืออยู่ ts.mergeforcecollapse (); ยืนยัน ts.stacksize == 1;ดูฟังก์ชั่นที่สำคัญกว่า
/ *** หากความยาวของการวิ่ง 2 ครั้งสุดท้ายถูกเพิ่มนานกว่าครั้งก่อนหน้าก่อนหน้านี้ให้ใช้การวิ่งที่ตำแหน่งกลางและการวิ่งที่มีความยาวด้านหน้าและด้านหลังสั้นกว่าเพื่อผสาน*หากความยาวของการวิ่ง 2 ครั้งสุดท้ายจะถูกเพิ่มสั้นกว่าก่อนหน้านี้ if (n> 0 && runlen [n-1] <= runlen [n] + runlen [n + 1]) {ถ้า (runlen [n-1] <runlen [n + 1]) n--; การควบรวมกิจการ (n); } อื่นถ้า (runlen [n] <= runlen [n + 1]) {mergeat (n); } else {break; // ค่าคงที่ถูกสร้างขึ้น}}