แผ่นวงเล็บคู่เรียงบนโต๊ะมิ้นต์ มีจุดสีอำพันที่แผ่นที่จับคู่ได้

LeetCode Valid Parentheses: สแต็กกับการจับคู่เครื่องหมาย

น้องกายEngineering

โจทย์ให้สตริงที่มีแค่ ()[]{} แล้วถามว่าเครื่องหมายเปิดปิดจับคู่และลำดับถูกต้องหรือไม่ เช่น "()[]{}" ผ่าน แต่ "(]" ไม่ผ่าน

แนวทางที่ 1: นับอย่างเดียว (ไม่พอ)

นับจำนวนเปิดกับปิดเท่ากันยังไม่พอ เพราะ ")(" นับเท่ากันแต่ผิด วิธีนี้ใช้สอนจุดพลาดได้ แต่ไม่ใช่คำตอบ

แนวทางที่ 2: สแต็กคลาสสิก (แนะนำหลัก)

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

นี่เป็นตัวอย่างโค้ดแบบ fragment / pseudocode สำหรับอธิบายแนวคิด:

def is_valid(s):
    pairs = {')': '(', ']': '[', '}': '{'}
    stack = []
    for ch in s:
        if ch in '([{':
            stack.append(ch)
        else:
            if not stack or stack[-1] != pairs[ch]:
                return False
            stack.pop()
    return not stack
  • เวลา O(n) พื้นที่ O(n)
  • อธิบายง่าย ตรงกับโครงสร้างข้อมูลพื้นฐาน

แนวทางที่ 3: แทนที่ซ้ำ ๆ

วนแทนที่ "()", "[]", "{}" ด้วยสตริงว่างจนหมดหรือไม่เปลี่ยน

  • เข้าใจง่ายมาก แต่เวลาแย่ลงเมื่อสตริงยาว เพราะสร้างสตริงใหม่บ่อย
  • เหมาะสาธิตแนวคิด ไม่เหมาะคำตอบหลักในสัมภาษณ์จังหวะจบ

แนวทางที่ 4: ตารางคู่แบบแมป

เหมือนสแต็ก แต่แยกแมปเปิดถึงปิดหรือปิดถึงเปิดให้ชัด ลด if ยาว ช่วยลดบั๊กตอนเขียนเร็ว

แนวทางที่ 5: พาร์เซอร์จริงในโปรดักชัน

ถ้าเป็นตัวตรวจนิพจน์จริง อาจใช้ไลบรารีพาร์เซอร์หรือ grammar แต่สำหรับโจทย์ LeetCode สแต็กยังสั้นและถูกที่สุด

แผนภาพด้านล่างเป็น fragment / pseudocode ของลำดับตรวจ:

flowchart TD
  A[อ่านตัวอักษร] --> B{เปิดหรือปิด?}
  B -->|เปิด| C[push เข้า stack]
  B -->|ปิด| D{บนสุดเป็นคู่ถูก?}
  D -->|ไม่| E[ไม่ผ่าน]
  D -->|ใช่| F[pop]
  C --> A
  F --> A
  A --> G{หมดสตริงและ stack ว่าง?}
  G -->|ใช่| H[ผ่าน]
  G -->|ไม่| E

เลือกใช้เมื่อไหร่

สถานการณ์แนะนำ
มือใหม่วาด stack บนกระดาษกับตัวอย่างสั้น
ไวท์บอร์ดสแต็ก + แมปคู่
โปรดักชันตรวจสตริงสั้นมากสแต็กเดียวกัน หรือรีใช้ไลบรารีถ้ามีกฎซับซ้อน

Recap

  • นับอย่างเดียวไม่พอ ต้องรักษาลำดับด้วย stack
  • จบแล้ว stack ต้องว่าง
  • เตรียมเคสสตริงว่าง ปิดก่อนเปิด และคู่คนละชนิด
  • พูด tradeoff พื้นที่ O(n) กับความถูกต้องให้ครบครับ