Notes & software courses · Free to learn
Aph's Blog
On this page

Binary Search & Variations

👋 อ่านฟรีทั้งหมดบน Aph's Blog — เนื้อหาภาษาไทย ทำตามทีละหน้าใน sidebar ได้เลย หากมีข้อเสนอแนะหรืออยากให้เพิ่มหัวข้อไหน บอกได้เสมอ

ค้นหาในข้อมูลที่เรียงแล้วด้วย O(log n) — และรูปแบบที่ดัดแปลงที่เจอบ่อย

binary search ค้นหาในข้อมูลที่เรียงแล้วโดยตัดครึ่งทุกครั้ง ทำให้เร็วมาก O(log n) (ข้อมูลล้านตัวใช้แค่ ~20 ครั้ง) เป็นเทคนิคที่เจอบ่อยและมีรูปแบบดัดแปลงหลายแบบ

binary search พื้นฐาน

python
def binary_search(arr, target):
    lo, hi = 0, len(arr) - 1
    while lo <= hi:
        mid = (lo + hi) // 2
        if arr[mid] == target:
            return mid           # เจอ คืน index
        elif arr[mid] < target:
            lo = mid + 1         # ตัดครึ่งซ้ายทิ้ง
        else:
            hi = mid - 1         # ตัดครึ่งขวาทิ้ง
    return -1                    # ไม่เจอ

print(binary_search([1, 3, 5, 7, 9], 7))   # 3
ข้อมูลต้องเรียงแล้วเท่านั้น

binary search ใช้ได้กับข้อมูลที่เรียงแล้วเท่านั้น และระวังกับดัก off-by-one (lo <= hi, mid+1, mid-1) กับ infinite loop — เป็นจุดที่พลาดบ่อยสุด ลองไล่ทีละขั้นด้วยตัวอย่างเล็ก ๆ

bisect — binary search สำเร็จรูป

Python มีโมดูล bisect ที่ทำ binary search ให้ หา "ตำแหน่งที่ควรแทรก" เพื่อคงการเรียง

python
import bisect

arr = [1, 3, 5, 7, 9]
print(bisect.bisect_left(arr, 5))    # 2  (ตำแหน่งของ 5)
print(bisect.bisect_right(arr, 5))   # 3  (หลัง 5)
print(bisect.bisect_left(arr, 6))    # 3  (ตำแหน่งที่ควรแทรก 6)

bisect.insort(arr, 6)    # แทรกแบบคงการเรียง
print(arr)               # [1, 3, 5, 6, 7, 9]

หา leftmost / rightmost

เมื่อมีค่าซ้ำ มักต้องหา "ตัวแรก" หรือ "ตัวสุดท้าย" ที่ตรงเงื่อนไข — ดัดแปลง binary search ด้วย bisect

python
import bisect
nums = [1, 2, 2, 2, 3]
# จำนวน 2 ทั้งหมด = ขอบขวา - ขอบซ้าย
left = bisect.bisect_left(nums, 2)    # 1
right = bisect.bisect_right(nums, 2)  # 4
print(right - left)                   # 3  (มี 2 อยู่ 3 ตัว)

สรุปหัวข้อนี้

  • binary search ตัดครึ่งทุกครั้ง O(log n) — ใช้กับข้อมูลเรียงแล้วเท่านั้น
  • ระวัง off-by-one (lo<=hi, mid±1) และ infinite loop
  • bisect: bisect_left/right หาตำแหน่ง, insort แทรกคงการเรียง
  • หา leftmost/rightmost ด้วย bisect — นับค่าซ้ำได้
แบบฝึกหัด

1) เขียน binary search เอง ทดสอบเคสเจอ/ไม่เจอ 2) ใช้ bisect หาตำแหน่งที่ควรแทรกค่า 3) นับจำนวนค่าที่ซ้ำด้วย bisect_left/right 4) อธิบายว่าทำไม binary search ต้องใช้ข้อมูลเรียงแล้ว