On this page
Big-O — วัดว่าโค้ดเร็วหรือช้า
ก่อนแตะโจทย์ข้อแรก ต้องรู้จัก Big-O ก่อน — วิธีบอกว่า "ถ้าข้อมูลใหญ่ขึ้น โค้ดเราจะช้าลงแค่ไหน" ทุกเฉลยในคอร์สนี้จะพูดถึงมัน
เวลาแก้โจทย์ เราไม่ได้สนแค่ "โค้ดตอบถูกไหม" แต่สนด้วยว่า "ถ้าข้อมูลใหญ่ขึ้นเป็นล้านตัว โค้ดยังทันไหม" Big-O คือภาษาที่ใช้พูดเรื่องนี้ มันไม่ใช่คณิตศาสตร์ยาก ๆ — แค่วิธีบอกว่า "งานเพิ่มขึ้นตามขนาดข้อมูลแบบไหน"
ไอเดียหลัก: นับว่าต้องทำงานกี่ครั้ง
สมมติมีลิสต์ยาว n ตัว เราถามว่า "โค้ดนี้ต้องแตะข้อมูลกี่ครั้งโดยประมาณ เมื่อเทียบกับ n" คำตอบนั้นแหละคือ Big-O เราสนแค่ "รูปทรงการโต" ไม่สนตัวเลขเป๊ะ ๆ (เช่น 2n กับ 5n ถือว่าโตแบบเดียวกัน = O(n))
nums = [4, 8, 15, 16, 23, 42] # n = 6
# แตะข้อมูลครั้งเดียว — ไม่ขึ้นกับ n เลย -> O(1)
print(nums[0])
# วนครบทุกตัว 1 รอบ -> O(n)
for x in nums:
print(x)
# วนซ้อนกัน 2 ชั้น ทุกคู่ -> O(n^2)
for a in nums:
for b in nums:
print(a, b)ระดับความเร็วที่เจอบ่อย (จากเร็วสุดไปช้าสุด)
| Big-O | ชื่อเรียก | ความหมายสั้น ๆ | ตัวอย่างในโจทย์ |
|---|---|---|---|
| O(1) | คงที่ | ทำงานเท่าเดิมไม่ว่าข้อมูลใหญ่แค่ไหน | หยิบสมาชิกตัวที่ i, เช็คใน set/dict |
| O(log n) | ลอการิทึม | ตัดปัญหาครึ่งหนึ่งทุกก้าว | Binary Search |
| O(n) | เชิงเส้น | วนดูข้อมูลรอบเดียว | หาค่ามากสุดในลิสต์ |
| O(n log n) | n log n | เร็วที่สุดของการเรียงข้อมูล | sorted(nums) |
| O(n^2) | กำลังสอง | วนซ้อนสองชั้น ทุกคู่ | เทียบทุกคู่ในลิสต์ |
| O(2^n) | เอ็กซ์โพเนนเชียล | ระเบิด — ลองทุกความเป็นไปได้ | Backtracking แบบดิบ ๆ |
O(1) และ O(log n) = เร็วมาก · O(n) และ O(n log n) = ดี ใช้ได้จริง · O(n^2) = เริ่มช้าเมื่อ n เป็นหมื่นขึ้นไป · O(2^n) = ใช้ได้แค่ n เล็ก ๆ เป้าหมายของเราคือทำโจทย์ให้ได้ Big-O ที่ดีที่สุดเท่าที่จะทำได้
ทำไมเราถึงตัดค่าคงที่ทิ้ง
ถ้าโค้ดวน 2 รอบเต็ม เราเขียน O(2n) แต่ Big-O สนแค่ "ทรงการโต" เมื่อ n ใหญ่มาก ๆ ค่าคงที่ 2 ไม่สำคัญ จึงเหลือ O(n) เหมือนกับที่ O(n^2 + n) เหลือแค่ O(n^2) เพราะพจน์ที่โตเร็วสุดครอบงำที่เหลือทั้งหมด
# ทั้งสองอันเป็น O(n) เหมือนกัน — ค่าคงที่ไม่นับ
for x in nums: # รอบที่ 1
print(x)
for x in nums: # รอบที่ 2
print(x)
# อันนี้ O(n^2) — พจน์ n^2 ครอบงำ n
for a in nums: # n^2
for b in nums:
pass
for x in nums: # + n -> ตัดทิ้ง เหลือ O(n^2)
passTime vs Space
Big-O ใช้วัดได้ 2 อย่าง: (1) Time — ใช้เวลา/จำนวนครั้งเท่าไร (2) Space — ใช้หน่วยความจำเพิ่มเท่าไร เช่น ถ้าเราสร้างลิสต์ใหม่ขนาดเท่า input ก็ใช้ space O(n) แต่ถ้าแก้ไขในที่เดิมโดยไม่สร้างของใหม่ ก็ O(1) เวลาแก้โจทย์เรามักต้องแลกกันระหว่างสองอย่างนี้
| โค้ด | Time | Space |
|---|---|---|
| หาผลรวมด้วย for รอบเดียว | O(n) | O(1) |
| สร้างลิสต์ใหม่ [x*2 for x in nums] | O(n) | O(n) |
| เอาทุกคู่มาเทียบกัน | O(n^2) | O(1) |
| ใส่ทุกตัวลง set เพื่อเช็คซ้ำ | O(n) | O(n) |
เคล็ดลับอ่าน Big-O จากโค้ดเร็ว ๆ
- ไม่มี loop เลย / หยิบด้วย index / เช็คใน set,dict → O(1)
- loop เดียววน n ตัว → O(n)
- loop ซ้อน loop (ทั้งคู่วิ่งตาม n) → O(n^2)
- แต่ละก้าวตัดข้อมูลเหลือครึ่ง → O(log n)
- มี sort อยู่ในนั้น → อย่างน้อย O(n log n)
- ใช้ dict/set เพื่อแลก space มาลด time จาก O(n^2) เหลือ O(n) — เป็นเทคนิคที่เจอบ่อยมาก
แก้ให้ "ถูก" ก่อนเสมอ แล้วค่อยถามว่า "เร็วกว่านี้ได้ไหม" อย่าเพิ่งไปเครียดกับ Big-O ที่ดีที่สุดตั้งแต่ยังเขียนไม่ออก — เขียนวิธีตรงไปตรงมาให้ผ่านก่อน แล้วค่อยปรับ