เทียบคู่ติดกัน ถ้าผิดก็สลับ
วน j จากซ้ายไปขวา เปรียบเทียบ arr[j] กับ arr[j+1] ทุกคู่ — ถ้า arr[j] > arr[j+1] ให้สลับทันที
Basic Sorting
เรียนรู้ Bubble Sort ตั้งแต่แนวคิด วิธีทำงานทีละขั้น พร้อมตัวอย่างโค้ดและแบบฝึกหัด
Bubble Sort คืออัลกอริทึมเรียงข้อมูลที่ทำงานโดยเปรียบเทียบสมาชิกที่อยู่ติดกันทีละคู่ — ถ้าคู่ไหนเรียงผิด (ตัวซ้ายมากกว่าตัวขวา) ก็สลับตำแหน่งทันที จากนั้นเลื่อนไปคู่ถัดไปเรื่อย ๆ จนสุดช่วง
หัวใจของ Bubble Sort เรียบง่ายมาก: "เทียบคู่ติดกัน ถ้าผิดก็สลับ" ทำซ้ำหลาย pass จนกว่าทั้งอาเรย์จะเรียงถูกต้อง
ชื่อ Bubble Sort มาจากพฤติกรรมที่ค่าใหญ่ ๆ จะค่อย ๆ ลอยไปทางขวาของอาเรย์ทีละขั้น เหมือนฟองอากาศที่ลอยขึ้นสู่ผิวน้ำ — ในแต่ละ pass ค่าสูงสุดที่ยังไม่เข้าที่จะถูกผลักไปอยู่ขวาสุดของช่วงที่ยังไม่ได้เรียง
ลองนึกภาพการจัดคิวคนตามความสูงจากเตี้ยไปสูง: เราเดินจากหัวแถวไปท้ายแถว ถ้าคนซ้ายสูงกว่าคนขวา ก็สลับที่กัน ทำแบบนี้หลายรอบ จนคนสูงสุดถูกผลักไปท้ายแถว แล้วคนสูงรองลงมาถูกผลักไปก่อนท้าย ไปเรื่อย ๆ จนทั้งแถวเรียงจากเตี้ยไปสูง
ค่ามากกว่าจะค่อย ๆ ลอยไปทางขวาในแต่ละ pass — เหมือนฟองอากาศลอยขึ้น
เราจะสาธิตด้วยอาเรย์ [5, 3, 8, 4, 2] โดยเป้าหมายคือเรียงจากน้อยไปมาก มาดูกันว่าเกิดอะไรขึ้นในแต่ละ pass
สังเกตว่าแต่ละ pass จะทำให้สมาชิกที่มีค่ามากที่สุดของช่วงที่ยังไม่เรียง ลอยไปอยู่ท้ายช่วงนั้น — ดังนั้นจำนวนสมาชิกที่ต้องเปรียบเทียบใน pass ถัดไปจะลดลงเรื่อย ๆ
โครงความคิดก่อนลงมือเขียนโค้ดจริง — วนลูปซ้อนเพื่อเปรียบเทียบและสลับคู่ที่ติดกัน
ฟังก์ชัน bubbleSort(values):
n = จำนวนสมาชิกใน values
ทำซ้ำ pass จาก 0 ถึง n - 2:
ทำซ้ำ j จาก 0 ถึง n - 2 - pass:
ถ้า values[j] > values[j + 1]:
สลับ values[j] กับ values[j + 1]
คืนค่า valuesเรียงจากน้อยไปมากด้วยการสลับสมาชิกที่อยู่ติดกัน
function bubbleSort(values) {
const arr = [...values];
for (let pass = 0; pass < arr.length - 1; pass += 1) {
for (let j = 0; j < arr.length - 1 - pass; j += 1) {
if (arr[j] > arr[j + 1]) {
const temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
return arr;
}
console.log(bubbleSort([5, 3, 8, 4, 2]));
// [2, 3, 4, 5, 8]อธิบายทีละส่วน: • `const arr = [...values]` — สร้างสำเนาอาเรย์ ไม่แก้ข้อมูลต้นฉบับโดยตรง • `for (let pass = 0; pass < arr.length - 1; pass += 1)` — ลูปนอกควบคุมจำนวน pass (n-1 pass ก็เพียงพอ) • `for (let j = 0; j < arr.length - 1 - pass; j += 1)` — ลูปในเปรียบเทียบสมาชิกติดกันทีละคู่ โดยลดขอบเขตลงตาม pass • `if (arr[j] > arr[j + 1])` — ถ้าตัวซ้ายมากกว่าตัวขวา ก็สลับทันที • ใช้ตัวแปร `temp` สำหรับการสลับค่าระหว่าง 2 ตำแหน่ง
ปรับปรุงได้อีก: หยุดก่อนเมื่อเรียงแล้ว
Bubble Sort พื้นฐานจะทำครบทุก pass เสมอ แม้อาเรย์จะเรียงแล้วตั้งแต่ pass แรก ๆ — แต่ถ้าเราเพิ่มตัวแปร `swapped` เพื่อตรวจว่ามีการสลับใน pass นี้หรือไม่ เราสามารถหยุด loop ก่อนได้ทันทีเมื่อข้อมูลเรียงแล้ว
เพิ่มตัวแปร swapped เพื่อตรวจจับ early exit — ถ้า pass ใดไม่มีการสลับเลย = เรียงแล้ว หยุดได้ทันที
function bubbleSortOptimized(values) {
const arr = [...values];
for (let pass = 0; pass < arr.length - 1; pass += 1) {
let swapped = false;
for (let j = 0; j < arr.length - 1 - pass; j += 1) {
if (arr[j] > arr[j + 1]) {
const temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
swapped = true;
}
}
if (!swapped) break;
}
return arr;
}
console.log(bubbleSortOptimized([1, 2, 3, 4, 5]));
// [1, 2, 3, 4, 5] — จบเร็วมากหลัง pass แรก
console.log(bubbleSortOptimized([5, 3, 8, 4, 2]));
// [2, 3, 4, 5, 8]ข้อดีของเวอร์ชัน optimized: ถ้าข้อมูลเกือบเรียงหรือเรียงอยู่แล้ว (Best Case) จะเหลือ Time Complexity เพียง O(n) เพราะ loop นอกจบตั้งแต่ pass แรก — เร็วกว่าเวอร์ชันพื้นฐานที่ต้องทำ n-1 pass เสมอ
ได้เวลาลงมือเขียน Bubble Sort ด้วยตัวเอง — เริ่มจากเขียนฟังก์ชันทำ 1 pass ก่อน แล้วค่อยประกอบเป็น Bubble Sort ที่สมบูรณ์
Space Complexity = O(1) — Bubble Sort เป็น in-place algorithm ใช้ตัวแปรชั่วคราว (temp, swapped) เพียงเล็กน้อยเท่านั้น ไม่ต้องสร้างโครงสร้างข้อมูลใหม่
| n (ขนาดข้อมูล) | Best Case (~n) | Average/Worst Case (~n²/2 รอบ) | Worst การสลับ |
|---|---|---|---|
| 10 | ~10 | ~45 | ~45 |
| 100 | ~100 | ~4,950 | ~4,950 |
| 1,000 | ~1,000 | ~499,500 | ~499,500 |
| 10,000 | ~10,000 | ~50M | ~50M |
| ข้อดี | ข้อเสีย | กรณีที่เหมาะสม |
|---|---|---|
| แนวคิดตรงไปตรงมา — เข้าใจง่ายที่สุดในกลุ่ม sorting algorithm พื้นฐาน | ช้ามากกับข้อมูลใหญ่ — O(n²) ทำให้ไม่เหมาะกับ n > 1,000 | การเรียนการสอนพื้นฐานอัลกอริทึม — แนะนำแนวคิดการเปรียบเทียบและสลับ |
| เขียนโค้ดง่าย — เหมาะสำหรับฝึกตรรกะลูปซ้อน (nested loop) | จำนวนการสลับสูง — สลับบ่อยกว่า Selection Sort มาก | ข้อมูลขนาดเล็ก (n < 100) — ที่ความเร็วไม่ใช่ปัจจัยสำคัญ |
| เป็น stable sort — ค่าเท่ากันรักษาลำดับเดิมไว้ได้ | มีวิธีที่เร็วกว่า — Merge Sort, Quick Sort ใช้ O(n log n) | ข้อมูลเกือบเรียง — ใช้เวอร์ชัน optimized เพื่อได้ O(n) |
| ตรวจจับ early exit ได้ — เวอร์ชัน optimized รู้ว่าเรียงแล้วและหยุดทันที | ไม่เหมาะกับข้อมูลที่สลับตำแหน่งมาก — Best Case ดีมาก แต่ Worst Case แย่มาก | อัลกอริทึมประกอบ — ใช้เป็นส่วนหนึ่งของอัลกอริทึมอื่น (เช่น Comb Sort) |
Bubble Sort, Selection Sort และ Insertion Sort เป็น 3 อัลกอริทึมเรียงข้อมูลพื้นฐานที่ใช้แนวคิดต่างกัน — ตารางนี้ช่วยให้คุณเลือกใช้ได้ถูกต้องตามสถานการณ์
| หัวข้อ | Bubble Sort | Selection Sort | Insertion Sort |
|---|---|---|---|
| แนวคิดหลัก | เปรียบเทียบ-สลับ สมาชิกที่ติดกัน | ค้นหา minimum แล้วสลับทีเดียว | หยิบ key แล้วแทรกในตำแหน่งที่ถูกต้อง |
| จำนวนการสลับ | มาก (สลับทุกครั้งที่ผิด) | น้อย (สูงสุด n-1 ครั้ง) | ปานกลาง (เลื่อนสมาชิกทีละช่อง) |
| Best Time | O(n) — แบบ optimized | O(n²) — ต้องหา min ทุกรอบ | O(n) — ข้อมูลเรียงแล้ว |
| Average Time | O(n²) | O(n²) | O(n²) |
| Worst Time | O(n²) | O(n²) | O(n²) |
| Stable | ใช่ | ไม่ (ในรูปแบบพื้นฐาน) | ใช่ |
| เหมาะกับ | ข้อมูลเล็ก + ต้องการโค้ดง่าย | จำนวนการสลับต้องน้อย | ข้อมูลเกือบเรียง หรือข้อมูลเข้ามาทีละตัว |
หัวใจสำคัญของ Bubble Sort ที่ต้องจำให้ขึ้นใจ
วน j จากซ้ายไปขวา เปรียบเทียบ arr[j] กับ arr[j+1] ทุกคู่ — ถ้า arr[j] > arr[j+1] ให้สลับทันที
หลัง pass ที่ k สมาชิก k ตัวทางขวาสุดเรียงถูกต้องแล้ว — ลูปในจึงลดขอบเขตลงตาม pass (arr.length - 1 - pass)
ถ้า pass ใดไม่มีการสลับเลย แปลว่าอาเรย์เรียงแล้ว — ใช้ `if (!swapped) break` เพื่อหยุดทันที ไม่ต้องทำ pass ที่เหลือ
Worst Case เกิดเมื่อข้อมูลเรียงกลับด้าน — ต้องสลับเกือบทุกคู่ในทุก pass Best Case เกิดเมื่อข้อมูลเรียงแล้วและใช้ optimized version
O(n²) ทำให้เวลาเพิ่มเร็วมากเมื่อ n ใหญ่ — ถ้ามีข้อมูลมากกว่า 1,000 ตัว ควรใช้ Merge Sort หรือ Quick Sort (O(n log n)) แทน
ทดสอบความเข้าใจก่อนไปต่อ
คิดถึงคุณสมบัติที่ว่า "ค่าสูงสุดลอยไปขวาสุด"