Mergesort
การเรียงลำดับแบบคงเสถียรภาพ
Mergesort เป็นบทเรียน C Academy ฟรีบน CoddyKit นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน C Academy และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส C Academy มีบทเรียนทั้งหมด 4 บทเรียน
การเรียงลำดับแบบคงลำดับเดิม
Mergesort คือการเรียงลำดับแบบแบ่งแล้วพิชิต โดยแบ่งอาร์เรย์ออกเป็นสองส่วน เรียงลำดับแต่ละส่วน แล้วผสานกลับเข้าด้วยกัน มีความซับซ้อนเป็น O(n log n) ในทุกกรณี และคงลำดับเดิม
ขั้นตอนการแบ่ง
แบ่งอาร์เรย์แบบเรียกซ้ำที่จุดกึ่งกลาง จนแต่ละส่วนมีสมาชิกหนึ่งตัว สมาชิกเพียงตัวเดียวถือว่าเรียงลำดับแล้วโดยปริยาย ซึ่งเป็นกรณีฐาน
ขั้นตอนการผสาน
การดำเนินการหลักคือการผสานช่วงข้อมูลที่เรียงลำดับแล้วสองช่วงให้เป็นช่วงเดียว ให้ตัวชี้ดัชนีเดินไปพร้อมกันทั้งสองช่วง และคัดลอกสมาชิกด้านหน้าที่มีค่าน้อยกว่าในแต่ละครั้ง
#include <stdio.h>
void merge(int a[], int lo, int mid, int hi, int tmp[]) {
int i = lo, j = mid + 1, k = lo;
while (i <= mid && j <= hi)
tmp[k++] = (a[i] <= a[j]) ? a[i++] : a[j++];
while (i <= mid) tmp[k++] = a[i++];
while (j <= hi) tmp[k++] = a[j++];
for (int t = lo; t <= hi; t++) a[t] = tmp[t];
}
int main(void) {
int a[] = {1, 4, 6, 2, 3, 5}; /* two sorted runs */
int tmp[6];
merge(a, 0, 2, 5, tmp);
for (int i = 0; i < 6; i++) printf("%d ", a[i]);
printf("\n");
return 0;
}เหตุผลที่คงลำดับเดิม
การผสานใช้ a[i] <= a[j] ดังนั้นเมื่อสมาชิกสองตัวมีค่าเท่ากัน จะเลือกสมาชิกจากช่วงซ้ายก่อน เนื่องจากช่วงซ้ายเก็บสมาชิกที่มาก่อน จึงรักษาลำดับเดิมไว้ได้
ตัวควบคุมการเรียกซ้ำ
Mergesort เรียกใช้ตัวเองแบบเรียกซ้ำกับแต่ละส่วน แล้วจึงผสาน เราส่งบัฟเฟอร์ชั่วคราวที่ใช้ร่วมกันเข้าไป เพื่อหลีกเลี่ยงการจองหน่วยความจำใหม่ทุกครั้งที่เรียก
#include <stdio.h>
void merge(int a[], int lo, int mid, int hi, int tmp[]) {
int i=lo, j=mid+1, k=lo;
while (i<=mid && j<=hi) tmp[k++] = (a[i]<=a[j]) ? a[i++] : a[j++];
while (i<=mid) tmp[k++]=a[i++];
while (j<=hi) tmp[k++]=a[j++];
for (int t=lo;t<=hi;t++) a[t]=tmp[t];
}
void msort(int a[], int lo, int hi, int tmp[]) {
if (lo >= hi) return;
int mid = lo + (hi - lo) / 2;
msort(a, lo, mid, tmp);
msort(a, mid + 1, hi, tmp);
merge(a, lo, mid, hi, tmp);
}
int main(void) {
int a[] = {5, 2, 9, 1, 3, 8, 4};
int tmp[7];
msort(a, 0, 6, tmp);
for (int i = 0; i < 7; i++) printf("%d ", a[i]);
printf("\n");
return 0;
}การใช้หน่วยความจำ
ต่างจาก quicksort ตรงที่ mergesort ต้องใช้หน่วยความจำเพิ่มเติม O(n) สำหรับบัฟเฟอร์การผสาน นี่คือข้อเสียหลักเมื่อใช้งานกับอาร์เรย์ขนาดใหญ่มากในสภาวะที่หน่วยความจำจำกัด
รับประกัน O(n log n)
การเรียกซ้ำจะแบ่งครึ่งเสมอ ทำให้มีระดับจำนวน log n ระดับ และแต่ละระดับจะผสานสมาชิก n ตัว ดังนั้น mergesort จึงมีความซับซ้อน O(n log n) ในกรณีดีที่สุด โดยเฉลี่ย และแย่ที่สุด ต่างจาก quicksort
การนับระดับการผสาน
จำนวนระดับของการเรียกซ้ำคือ ceil(log2 n) มาคำนวณสำหรับขนาดหลายค่า
#include <stdio.h>
int levels(int n) {
int L = 0;
while (n > 1) { n = (n + 1) / 2; L++; }
return L;
}
int main(void) {
int sizes[] = {1, 2, 8, 100, 1000};
for (int i = 0; i < 5; i++)
printf("n=%d levels=%d\n", sizes[i], levels(sizes[i]));
return 0;
}Mergesort จากล่างขึ้นบน
รูปแบบที่วนซ้ำนี้จะผสานช่วงข้อมูลขนาด 1 จากนั้นเป็น 2 แล้วจึงเป็น 4 โดยเพิ่มขนาดเป็นสองเท่าในแต่ละรอบ วิธีนี้ไม่ใช้การเรียกซ้ำเลย และเหมาะกับลิงก์ลิสต์
#include <stdio.h>
void merge(int a[], int lo, int mid, int hi, int tmp[]) {
int i=lo,j=mid+1,k=lo;
while(i<=mid&&j<=hi) tmp[k++]=(a[i]<=a[j])?a[i++]:a[j++];
while(i<=mid) tmp[k++]=a[i++];
while(j<=hi) tmp[k++]=a[j++];
for(int t=lo;t<=hi;t++) a[t]=tmp[t];
}
int main(void) {
int a[] = {5, 2, 9, 1, 3, 8}, n = 6, tmp[6];
for (int width = 1; width < n; width *= 2)
for (int lo = 0; lo < n - width; lo += 2 * width) {
int mid = lo + width - 1;
int hi = (lo + 2*width - 1 < n-1) ? lo + 2*width - 1 : n-1;
merge(a, lo, mid, hi, tmp);
}
for (int i = 0; i < n; i++) printf("%d ", a[i]);
printf("\n");
return 0;
}เมื่อใดควรเลือก Mergesort
ควรเลือกใช้ mergesort เมื่อคุณต้องการ:
- O(n log n) ที่รับประกันได้ โดยไม่มีกรณีเลวร้าย
- การคงลำดับเดิม
- การเรียงลำดับลิงก์ลิสต์ (ไม่จำเป็นต้องเข้าถึงแบบสุ่ม)
- การเรียงลำดับภายนอกสำหรับข้อมูลที่มีขนาดใหญ่เกินกว่าจะเก็บใน RAM
Mergesort เทียบกับ Quicksort
Quicksort มักทำงานได้เร็วกว่าในทางปฏิบัติและเรียงลำดับภายในพื้นที่เดิม แต่ไม่คงลำดับเดิมและอาจมีกรณีแย่ที่สุดที่ใช้เวลามาก ส่วน mergesort คงลำดับเดิมและมีขอบเขตเวลาที่รับประกันได้ แต่ใช้หน่วยความจำเพิ่มเติม ให้เลือกตามข้อจำกัดของคุณ
ตรวจสอบความเข้าใจอย่างรวดเร็ว
ทดสอบความเข้าใจของคุณเกี่ยวกับ mergesort
สรุปทบทวน
คุณได้เรียนรู้เกี่ยวกับ mergesort
- แบ่งครึ่ง เรียงลำดับแต่ละส่วน แล้วผสาน
- การผสานจะเลือกคีย์ที่อยู่ทางซ้ายก่อนเมื่อค่าเท่ากัน ทำให้คงลำดับเดิม
- รับประกัน O(n log n) ในทุกกรณี
- ใช้หน่วยความจำเพิ่มเติม O(n) เหมาะอย่างยิ่งกับลิงก์ลิสต์และการเรียงลำดับภายนอก
คำถามที่พบบ่อย
บทเรียน “Mergesort” ฟรีหรือไม่
ใช่ — ข้อความเต็มของ “Mergesort” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส C Academy ให้อัปเกรดเป็น CoddyKit PRO คอร์ส C Academy มีบทเรียนทั้งหมด 4 บทเรียน
คุณจะเรียนรู้อะไรในบทเรียน “Mergesort”
การเรียงลำดับแบบคงเสถียรภาพ คุณปฏิบัติ C Academy ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน
คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน C Academy หรือไม่
ไม่จำเป็นต้องมีประสบการณ์มาก่อน C Academy บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน
บทเรียน “Mergesort” ใช้เวลานานแค่ไหน
บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย
ฉันเขียนและรันโค้ดในบทเรียน C Academy นี้ได้ไหม
ได้ บทเรียน C Academy ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ