ข้อมูลต้องเรียงก่อนเสมอ
ถ้าอาเรย์ไม่เรียง Binary Search จะทำงานผิด — ต้องเป็น sorted array เท่านั้น
Searching
เรียนรู้ Binary Search ตั้งแต่แนวคิดการตัดครึ่ง, เงื่อนไข sorted array, ตัวแปร left/right/mid, โค้ด JavaScript, Code Trace, Time/Space Complexity, ข้อผิดพลาดที่พบบ่อย และแบบฝึกหัด 3 ข้อ
Binary Search (การค้นหาแบบทวิภาค) คืออัลกอริทึมค้นหาที่ทรงพลังสำหรับข้อมูลที่เรียงลำดับแล้ว — แทนที่จะไล่ตรวจทีละตัว (แบบ Linear Search) เราจะดูตำแหน่งกลางก่อน แล้วตัดช่วงค้นหาทิ้งครึ่งหนึ่งทันที
พูดอีกแบบ: Linear Search เหมือนเดินตรวจทุกห้องในอาคารทีละห้อง แต่ Binary Search เหมือนถามตำแหน่งกลางของช่วงที่เหลือ แล้วปิดครึ่งอาคารที่ไม่เกี่ยวทิ้งในทุกครั้งที่ตัดสินใจ
หัวใจของ Binary Search คือทุกรอบที่เปรียบเทียบ เราจะตัดข้อมูลทิ้งประมาณครึ่งหนึ่งเสมอ — ทำให้จำนวนรอบที่ต้องตรวจเติบโตช้ามาก (logarithmic) แม้ข้อมูลจะมีขนาดใหญ่ขึ้นหลายเท่า
ตัวอย่าง: ถ้าอาเรย์มี 1,000,000 ตัว — Linear Search Worst Case ต้องดู 1,000,000 ครั้ง แต่ Binary Search ใช้เพียง ~20 ครั้งเท่านั้น
⚠️ ต้องเรียงข้อมูลก่อนใช้ Binary Search เสมอ
Binary Search อาศัยความสัมพันธ์ว่า "ค่าทุกตัวด้านซ้ายของตำแหน่งกลางมีค่าน้อยกว่า และค่าทุกตัวด้านขวามีค่ามากกว่า" — ดังนั้นข้อมูลต้องเป็น sorted array เท่านั้น ถ้าข้อมูลยังไม่เรียง อัลกอริทึมจะทำงานผิดพลาด
หากข้อมูลของคุณยังไม่เรียงลำดับ มีทางเลือก: • ใช้ Linear Search — ไม่ต้องเรียงก่อน (เหมาะกับข้อมูลน้อย) • จัดเรียงข้อมูลก่อนด้วย .sort() แล้วใช้ Binary Search (คุ้มค่าถ้าต้องค้นหาหลายครั้งในข้อมูลชุดเดียวกัน) • จัดเก็บข้อมูลให้เรียงตั้งแต่แรก — ถ้าควบคุมวิธีการเก็บข้อมูลได้
Binary Search ใช้ตัวแปร 3 ตัวควบคุมช่วงค้นหา — สองตัวเป็นขอบเขตที่หดเข้าหากันเรื่อย ๆ และหนึ่งตัวเป็นจุดเปรียบเทียบ
ทุกครั้งที่เปรียบเทียบ เราจะอัปเดต left หรือ right เพื่อ"บีบ"ช่วงค้นหาให้แคบลง — จนกว่าจะเจอเป้าหมาย หรือ left > right (ช่วงค้นหาว่างเปล่า = ไม่พบ)
ค้นหา target = 23 ใน sorted array [3, 7, 10, 14, 18, 23, 29, 31, 40] — มาดูกันว่า Binary Search ตัดช่วงค้นหาอย่างไร
Binary Search ลดช่วงค้นหาลงครึ่งหนึ่งในแต่ละรอบ — จาก 9 ตัว → เหลือแค่ไม่กี่ตัว
โครงแนวคิดก่อนลงมือเขียนโค้ดจริง — สังเกตการอัปเดต left/right ให้มาบรรจบกัน
ฟังก์ชัน binarySearch(values, target):
left = 0
right = values.length - 1
ขณะที่ left <= right:
mid = left + floor((right - left) / 2)
ถ้า values[mid] == target:
คืนค่า mid
ถ้า values[mid] < target:
left = mid + 1
มิฉะนั้น:
right = mid - 1
คืนค่า -1รูปแบบ standard ที่ใช้ left/right/mid — เป็นรูปแบบ iterative ใช้ memory O(1)
function binarySearch(values, target) {
let left = 0;
let right = values.length - 1;
while (left <= right) {
const mid = left + Math.floor((right - left) / 2);
if (values[mid] === target) {
return mid;
}
if (values[mid] < target) {
left = mid + 1;
} else {
right = mid - 1;
}
}
return -1;
}
// ตัวอย่างการใช้งาน
const numbers = [3, 7, 10, 14, 18, 23, 29, 31, 40];
console.log(binarySearch(numbers, 23)); // 5
console.log(binarySearch(numbers, 3)); // 0
console.log(binarySearch(numbers, 40)); // 8
console.log(binarySearch(numbers, 11)); // -1
console.log(binarySearch([], 99)); // -1อธิบายทีละส่วน: • `let left = 0; let right = values.length - 1` — ตั้งขอบเขตเริ่มต้นคลุมทั้งอาเรย์ • `while (left <= right)` — วน loop ตราบใดที่ช่วงค้นหายังไม่ว่าง (left ยังไม่เกิน right) • `mid = left + Math.floor((right - left) / 2)` — คำนวณตำแหน่งกลางแบบป้องกัน overflow • ถ้า `values[mid] === target` → return mid (เจอแล้ว) • ถ้า `values[mid] < target` → เป้าหมายอยู่ทางขวา → เลื่อน left ไป mid + 1 • ถ้า `values[mid] > target` → เป้าหมายอยู่ทางซ้าย → เลื่อน right ไป mid - 1 • หลัง loop → return -1 (ช่วงว่าง = ไม่มี target ในอาเรย์)
Space Complexity = O(1) — ฟังก์ชันแบบ iterative ใช้ตัวแปร left, right, mid เท่านั้น ไม่ต้องสร้างโครงสร้างข้อมูลใหม่
| n (ขนาดอาเรย์) | log₂(n) (สูงสุดกี่รอบ) | Linear Search (รอบ) |
|---|---|---|
| 10 | ~4 | 10 |
| 100 | ~7 | 100 |
| 1,000 | ~10 | 1,000 |
| 1,000,000 | ~20 | 1,000,000 |
| 1,000,000,000 | ~30 | 1,000,000,000 |
| ข้อดี | ข้อจำกัด | กรณีที่เหมาะสม |
|---|---|---|
| เร็วมากกับข้อมูลใหญ่ — O(log n) | ใช้ได้กับ sorted array เท่านั้น | ข้อมูลเรียงลำดับแล้ว และมีขนาดใหญ่ |
| ใช้หน่วยความจำคงที่ O(1) | ถ้ายังไม่เรียง ต้องเสียเวลาจัดเรียงก่อน | ต้องการค้นหาหลายครั้งในข้อมูลชุดเดิม |
| เป็นพื้นฐานของเทคนิคอื่น (lower/upper bound, search insert) | ไม่เหมาะกับ Linked List (เข้าถึงตำแหน่งกลางช้า O(n)) | ฐานข้อมูล, dictionary, autocomplete ที่ข้อมูลเรียงไว้แล้ว |
| หัวข้อ | Linear Search | Binary Search |
|---|---|---|
| ต้องเรียงก่อนหรือไม่ | ไม่จำเป็น | จำเป็น |
| แนวคิด | ไล่ทีละตำแหน่ง | ตัดครึ่งช่วงค้นหา |
| Best Time | O(1) | O(1) |
| Average Time | O(n) | O(log n) |
| Worst Time | O(n) | O(log n) |
| Space | O(1) | O(1) |
| n = 1,000,000 | สูงสุด 1,000,000 ครั้ง | สูงสุด ~20 ครั้ง |
| เหมาะกับ | ข้อมูลน้อย หรือยังไม่เรียง | ข้อมูลใหญ่ที่เรียงแล้ว — ค้นหาบ่อย |
หัวใจสำคัญของ Binary Search ที่ต้องจำให้ขึ้นใจ
ถ้าอาเรย์ไม่เรียง Binary Search จะทำงานผิด — ต้องเป็น sorted array เท่านั้น
สูตรนี้ป้องกัน integer overflow ได้ดีกว่า (left + right) / 2 โดยตรง
ต้อง +1 และ -1 เสมอ — ถ้าลืมจะเกิด infinite loop เพราะ mid อาจไม่เปลี่ยน
ใช้ <= เพื่อให้ตรวจตัวสุดท้ายเมื่อ left === right ด้วย ไม่เช่นนั้นอาจพลาด target
เหมือน Linear Search — ค่าที่คืนคือตำแหน่ง ไม่ใช่ boolean และ -1 แปลว่าไม่พบ
ทุกรอบลดช่วงค้นหาลงครึ่งหนึ่ง — n = 1,000,000 → แค่ ~20 รอบ
ทดสอบความเข้าใจก่อนเริ่มทำแบบฝึกหัด
คิดถึงแนวคิดหลักที่ทำให้ประสิทธิภาพเป็น O(log n)
แบบฝึกหัด 3 ข้อนี้จะช่วยให้คุณเขียน Binary Search ได้คล่อง — เริ่มจากการค้นหามาตรฐาน ไปจนถึงการหา first/last position และ search insert