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

ข้อ 19 · LC724 Find Pivot Index (หา pivot index) 🟢

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

หา index ที่ผลรวมฝั่งซ้ายเท่ากับฝั่งขวา โดยไม่บวกซ้ำทุกครั้ง

โจทย์ (LC724): กำหนด array จำนวนเต็ม nums ให้คำนวณหา pivot index (ดัชนีจุดสมดุล) ของ array นี้ pivot index คือ index ที่ผลรวมของตัวเลขทั้งหมดที่อยู่ทางซ้ายของ index นั้น (strictly) เท่ากับผลรวมของตัวเลขทั้งหมดที่อยู่ทางขวาของ index นั้น (strictly) ถ้า index อยู่ริมซ้ายสุดของ array ให้ถือว่าผลรวมฝั่งซ้ายเป็น 0 เพราะไม่มีสมาชิกอยู่ทางซ้ายเลย กรณีริมขวาสุดก็เช่นเดียวกัน ให้ return pivot index ที่อยู่ซ้ายสุด ถ้าไม่มี index แบบนี้เลยให้ return -1

Example 1
Input:
nums = [1, 7, 3, 6, 5, 6]
Output:
3
Explanation:
pivot index คือ 3: ผลรวมซ้าย = 1 + 7 + 3 = 11 เท่ากับผลรวมขวา = 5 + 6 = 11
Example 2
Input:
nums = [1, 2, 3]
Output:
-1
Explanation:
ไม่มี index ไหนใน array นี้ที่ทำให้ผลรวมสองฝั่งสมดุลกันได้
Example 3
Input:
nums = [2, 1, -1]
Output:
0
Explanation:
pivot index คือ 0: ผลรวมซ้ายว่างจึงเป็น 0 เท่ากับผลรวมขวา = 1 + (-1) = 0
Constraints (ข้อจำกัด)
  • 1 <= nums.length <= 10^4
  • -1000 <= nums[i] <= 1000

ต้อง return index ซ้ายสุดที่สมดุล และฝั่งที่ว่าง (ก่อนตัวแรกหรือหลังตัวสุดท้าย) ถือว่ามีผลรวมเป็น 0

แนวทาง — ต้องใช้อะไร & คิดยังไง

ใช้ไอเดีย prefix sum (ผลรวมสะสม) แต่ครั้งนี้เราสนใจผลรวมสองฝั่งของแต่ละ index วิธี naive (ตรงตัว) คือทุก index ก็บวกฝั่งซ้ายใหม่และฝั่งขวาใหม่ ซึ่งกลายเป็น O(n^2) ช้าเกินไปเมื่อ array ยาว

ทริกคือรู้ผลรวมทั้งหมด total ไว้ก่อน แล้ว accumulate (สะสม) เฉพาะฝั่งซ้าย left_sum ระหว่าง iterate (เดิน) เมื่ออยู่ที่ index i ผลรวมฝั่งขวาหาได้ทันทีจาก total - left_sum - nums[i] ไม่ต้องบวกฝั่งขวาใหม่เลย

  1. คำนวณ total = ผลรวมทั้ง array (หนึ่งรอบ)
  2. initialize (ตั้งค่าเริ่มต้น) left_sum = 0 เก็บผลรวมของตัวที่อยู่ทางซ้ายของ i
  3. iterate (วน) ทีละ index i พร้อมค่า n = nums[i]: คำนวณ right_sum = total - left_sum - n
  4. ถ้า left_sum == right_sum แปลว่าเจอ pivot return i ทันที (ซ้ายสุดโดยธรรมชาติ)
  5. ถ้ายังไม่สมดุล บวก n เข้า left_sum แล้วไปตัวถัดไป
  6. จบ loop ยังไม่เจอ return -1
จุดพลาดที่พบบ่อย

ลำดับสำคัญมาก ต้องเช็คสมดุล ก่อน แล้วค่อยบวก n เข้า left_sum เพราะตอนอยู่ที่ index i ตัว nums[i] ยังไม่นับเป็นฝั่งซ้าย ถ้าบวกก่อนเช็คจะได้คำตอบผิด

ไล่ทีละสเต็ป

iterate nums = [1, 7, 3, 6, 5, 6] ซึ่ง total = 28

inleft_sum (ก่อนเช็ค)right_sumสมดุล?
01028-0-1 = 27ไม่
17128-1-7 = 20ไม่
23828-8-3 = 17ไม่
361128-11-6 = 11ใช่ → คืน 3
▶ เฉลยละเอียด (ลองเองก่อนนะ)
เฉลย (Python) — โค้ดนี้รันได้จริงpython
def pivot_index(nums):
    total = sum(nums)   # ผลรวมทั้งหมด (จ่าย O(n) ครั้งเดียว)
    left_sum = 0        # ผลรวมของตัวที่อยู่ทางซ้ายของ i
    for i, n in enumerate(nums):
        # ผลรวมขวา = total - ฝั่งซ้าย - ตัวปัจจุบัน
        right_sum = total - left_sum - n
        if left_sum == right_sum:  # เช็คก่อนบวก n
            return i               # เจอ pivot ซ้ายสุด
        left_sum += n   # เลื่อน i ไปขวา ตัวปัจจุบันจึงกลายเป็นฝั่งซ้าย
    return -1           # ไล่จนจบไม่เจอ

print(pivot_index([1, 7, 3, 6, 5, 6]))  # 3
print(pivot_index([1, 2, 3]))           # -1
print(pivot_index([2, 1, -1]))          # 0
Output
3
-1
0

หัวใจคือเราไม่อยากคำนวณผลรวมซ้าย-ขวาใหม่ทุก index (จะกลายเป็น O(n^2)) จึงเก็บ left_sum ที่ accumulate (สะสม) มาจากซ้าย ส่วนผลรวมขวาคำนวณได้จาก total ที่รู้ล่วงหน้า ลบด้วยฝั่งซ้ายและตัวปัจจุบันออก จึงเหลือแค่การ iterate รอบเดียว

edge case (กรณีขอบ) ที่ต้องระวังคือ pivot อยู่ที่หัวหรือท้าย array เช่น [2, 1, -1] ตอบ 0 เพราะฝั่งซ้ายว่าง (ผลรวม 0) เท่ากับฝั่งขวา 1+(-1) = 0 โค้ดนี้รองรับเพราะ left_sum เริ่มที่ 0 อยู่แล้ว ถ้าเผลอสลับไปบวก n ก่อนเช็ค index 0 จะไม่มีทางถูกตรวจในสภาพฝั่งซ้ายว่าง

Time O(n) หา total หนึ่งรอบ แล้ว iterate อีกหนึ่งรอบ · Space O(1) ใช้ตัวแปรสะสมเท่านั้น

💡 สรุป pattern

เมื่อโจทย์ถามความสัมพันธ์ระหว่าง ผลรวมฝั่งซ้าย กับ ฝั่งขวา ที่แต่ละจุด ให้รู้ total ก่อนแล้ว accumulate ฝั่งเดียว ฝั่งตรงข้ามได้มาจากการลบ เทคนิคนี้เปลี่ยน O(n^2) ให้เหลือ O(n) ในหลายโจทย์