งูต้นฉบับกำลังย้ายการ์ดคะแนนสูงสุดขึ้นยอดกองทรงต้นไม้บนโต๊ะสีมิ้นต์โดยไม่กลับป้ายคะแนน

Python 3.14 max-heap API: เลิกใส่เครื่องหมายลบเพื่อจัดคิวค่าสูงสุด

พี่โชว์Engineering

ก่อน Python 3.14 คนทำ max-heap มักเก็บตัวเลขติดลบใน min-heap ครับ เทคนิคนี้ใช้ได้ แต่ทำให้ domain value กับ storage value คนละความหมาย และพลาดง่ายเมื่อ tuple มีหลาย field Python 3.14 เพิ่ม heapify_max, heappush_max, heappop_max และคู่ replace/pushpop ให้เรียกตรงเจตนา

WHY: ลบ mental translation ออกจากโค้ด

ถ้าคะแนนจริงคือ 90 แต่ใน heap เก็บ -90 คนอ่านต้องพลิกป้ายในหัวทุกครั้ง เหมือนลิฟต์เขียนชั้นบนสุดเป็นเลขติดลบ ยิ่งมี priority, timestamp และ id ใน tuple กฎ tie-break ยิ่งอ่านยาก API max-heap ทำให้ข้อมูลใน list ยังเป็นค่าจริง และ root คือค่าสูงสุด

HOW: รักษา top priority ด้วย API ชุดเดียว

import heapq
import unittest


class MaxHeapTests(unittest.TestCase):
    def test_highest_priority_is_popped_first(self) -> None:
        priorities = [30, 10, 80, 50]
        heapq.heapify_max(priorities)
        heapq.heappush_max(priorities, 70)

        popped = [heapq.heappop_max(priorities) for _ in range(3)]
        self.assertEqual(popped, [80, 70, 50])

    def test_pushpop_keeps_new_top(self) -> None:
        priorities = [90, 60, 40]
        heapq.heapify_max(priorities)
        removed = heapq.heappushpop_max(priorities, 50)
        self.assertEqual(removed, 90)
        self.assertEqual(priorities[0], 60)


if __name__ == "__main__":
    unittest.main()

อย่าผสมฟังก์ชัน min-heap กับ max-heap บน list เดียวกัน invariant หรือกฎการจัดกองจะเสียโดยไม่จำเป็นต้อง error ทันที ตั้งชื่อ helper หรือห่อ heap ใน class เล็ก ๆ เมื่อมีหลายทีมแตะข้อมูลชุดเดียวกัน

เลือก operation ให้ตรงงาน

heappushpop_max ใส่ค่าใหม่แล้วคืนค่าสูงสุดอย่างมีประสิทธิภาพ ส่วน heapreplace_max เอา root เดิมออกก่อนแล้วใส่ค่าใหม่ ความต่างสำคัญเมื่อค่าใหม่สูงกว่า root เดิม จึงเขียน test ที่ครอบสองด้านของ threshold

ถ้าต้องการคิวงาน concurrent heapq ไม่ใส่ lock ให้เอง ให้ใช้ queue.PriorityQueue หรือจัด synchronization รอบ list และจำไว้ว่า max-heap API ต้องใช้ Python 3.14 ขึ้นไป โปรเจกต์ที่รองรับรุ่นเก่าต้องคง compatibility layer ชัดเจน

Recap

  • ใช้ max-heap API เพื่อเก็บค่าจริง ไม่ต้อง negate
  • ห้ามผสม min/max operations ใน heap เดียว
  • แยก pushpop กับ replace ด้วย test
  • ใส่ lock หรือใช้ queue เมื่อมีหลาย thread