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

การค้นหา (Searching)

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

Linear search ไล่ดูทีละตัว O(n) เทียบกับ Binary search แบ่งครึ่ง O(log n) ที่เร็วกว่ามากบนข้อมูลที่เรียงแล้ว

การค้นหาข้อมูลคือสิ่งที่ทำบ่อยที่สุดในโปรแกรม มี 2 อัลกอริทึมพื้นฐานที่ต้องรู้ และความต่างของมันคือตัวอย่างที่ดีที่สุดของพลัง Big-O

Linear Search — ไล่ดูทีละตัว O(n)

วิธีตรงไปตรงมาที่สุด: ดูทีละตัวตั้งแต่ต้นจนเจอ ใช้ได้กับข้อมูลทุกแบบ ไม่ต้องเรียงก่อน แต่ถ้าข้อมูลมีล้านตัวและเป้าหมายอยู่ท้าย ก็ต้องดูล้านครั้ง

python
def linear_search(arr, target):
    for i in range(len(arr)):
        if arr[i] == target:
            return i        # เจอ คืน index
    return -1               # ไม่เจอ

print(linear_search([4, 2, 7, 1, 9], 7))  # 2
print(linear_search([4, 2, 7, 1, 9], 5))  # -1

# Python มี in และ index() ให้ใช้อยู่แล้ว (ก็เป็น O(n))
print(7 in [4, 2, 7])       # True

Binary Search — แบ่งครึ่งค้นหา O(log n)

ใช้ได้เฉพาะกับข้อมูลที่ "เรียงแล้ว" หลักการคือดูค่ากลาง ถ้าน้อยไปตัดครึ่งซ้ายทิ้ง ถ้ามากไปตัดครึ่งขวาทิ้ง ทุกรอบตัดข้อมูลเหลือครึ่ง เหมือนเปิดพจนานุกรมหาคำ ไม่ได้เปิดทีละหน้า

python
def binary_search(arr, target):
    left, right = 0, len(arr) - 1
    while left <= right:
        mid = (left + right) // 2   # ตำแหน่งกลาง
        if arr[mid] == target:
            return mid              # เจอ
        elif arr[mid] < target:
            left = mid + 1          # เป้าอยู่ครึ่งขวา
        else:
            right = mid - 1         # เป้าอยู่ครึ่งซ้าย
    return -1

print(binary_search([1, 3, 5, 7, 9, 11], 7))   # 3
print(binary_search([1, 3, 5, 7, 9, 11], 4))   # -1
log n เร็วแค่ไหน

ข้อมูล 1,000,000 ตัว linear search ต้องดูสูงสุด 1 ล้านครั้ง แต่ binary search ดูแค่ ~20 ครั้ง! เพราะทุกรอบตัดเหลือครึ่ง (1M → 500k → 250k → ...) นี่คือพลังของ O(log n)

เงื่อนไขสำคัญ

Binary search ใช้ได้ก็ต่อเมื่อข้อมูลเรียงแล้วเท่านั้น ถ้ายังไม่เรียงต้อง sort ก่อน (O(n log n)) ระวัง off-by-one ตรง mid + 1 และ mid - 1 ซึ่งเป็นจุดที่พลาดบ่อย

เปรียบเทียบ

Linear SearchBinary Search
Big-OO(n)O(log n)
ต้องเรียงก่อนไหมไม่ต้องต้อง
1 ล้านตัว (worst)~1,000,000 ครั้ง~20 ครั้ง
เหมาะเมื่อข้อมูลน้อย/ไม่เรียงข้อมูลเยอะและเรียงแล้ว
เร็วกว่า binary search ก็มี: dict/set (O(1))

ถ้าแค่ต้องเช็คว่า "มีค่านี้ไหม" บ่อย ๆ ไม่ต้องเรียงแล้ว binary search — โยนลง set/dict แล้วใช้ x in s ได้ O(1) เลย (เร็วกว่า O(log n) อีก) เลือก binary search เมื่อข้อมูลเรียงอยู่แล้วหรือต้องหา "ตำแหน่ง/ขอบเขต" ส่วนเช็คสมาชิกล้วน ๆ ใช้ set

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

  • Linear search ไล่ดูทีละตัว O(n) ใช้ได้กับข้อมูลทุกแบบ ไม่ต้องเรียง
  • Binary search แบ่งครึ่งทุกรอบ O(log n) เร็วกว่ามาก แต่ข้อมูลต้องเรียงแล้ว
  • ทุกรอบของ binary search ตัดข้อมูลเหลือครึ่ง — ล้านตัวค้นแค่ ~20 ครั้ง
  • ระวัง off-by-one ตรง mid+1 / mid-1
แบบฝึกหัด

1) เขียน linear search คืน index ของค่าที่หา 2) เขียน binary search เอง แล้วทดสอบกับลิสต์เรียงแล้ว 3) นับว่า binary search บนลิสต์ 1000 ตัว ใช้กี่รอบ (ลองใส่ print นับ) 4) ดัดแปลง binary search ให้หา "ค่าแรกที่มากกว่าหรือเท่ากับ target"