นากกายสีมิ้นต์คูณกองตัวเลขซ้ายและขวาแล้ววางผลกลางบนโต๊ะมิ้นต์

LeetCode #238 Product of Array Except Self: คูณซ้ายขวาโดยไม่ใช้หาร

น้องกายEngineering

โจทย์ให้ nums ความยาว n แล้วคืนอาร์เรย์ answer ที่ answer[i] เท่ากับผลคูณทุกตัวยกเว้น nums[i] ข้อจำกัดสำคัญคือ ห้ามใช้การหาร และ ideally เวลา O(n)

วิธีที่ 1: คูณทั้งหมดแล้วหาร (ไม่ผ่านโจทย์มาตรฐาน)

หาผลคูณรวม แล้วแต่ละช่องเป็น total / nums[i] พังเมื่อมีศูนย์ และผิดกติกาที่ห้ามหาร เก็บไว้เป็น anti-pattern

วิธีที่ 2: prefix และ suffix อาร์เรย์

prefix[i] = คูณทุกตัวก่อน i, suffix[i] = คูณทุกตัวหลัง i แล้ว answer[i] = prefix[i] * suffix[i] เวลา O(n) พื้นที่ O(n) นอกจากผลลัพธ์

วิธีที่ 3: เขียนลงคำตอบสองผ่าน (แนะนำหลัก)

ผ่านซ้ายไปขวาสะสมคูณซ้ายลง answer แล้วผ่านขวาไปซ้ายคูณด้วยคูณขวาที่สะสมในตัวแปรเดียว เวลา O(n) พื้นที่เสริม O(1) ตามนิยามโจทย์ที่ผลลัพธ์ไม่นับ

import unittest


def product_except_self(nums):
    n = len(nums)
    answer = [1] * n
    left = 1
    for i in range(n):
        answer[i] = left
        left *= nums[i]
    right = 1
    for i in range(n - 1, -1, -1):
        answer[i] *= right
        right *= nums[i]
    return answer


def product_except_self_prefix_suffix(nums):
    n = len(nums)
    prefix = [1] * n
    suffix = [1] * n
    for i in range(1, n):
        prefix[i] = prefix[i - 1] * nums[i - 1]
    for i in range(n - 2, -1, -1):
        suffix[i] = suffix[i + 1] * nums[i + 1]
    return [prefix[i] * suffix[i] for i in range(n)]


class ProductExceptSelfTests(unittest.TestCase):
    def test_examples(self):
        cases = [
            ([1, 2, 3, 4], [24, 12, 8, 6]),
            ([-1, 1, 0, -3, 3], [0, 0, 9, 0, 0]),
            ([2, 3], [3, 2]),
        ]
        for nums, expected in cases:
            with self.subTest(nums=nums):
                self.assertEqual(product_except_self(nums), expected)
                self.assertEqual(product_except_self_prefix_suffix(nums), expected)

    def test_two_zeros(self):
        self.assertEqual(product_except_self([0, 0, 2]), [0, 0, 0])


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

เลือกตามสถานการณ์

สถานการณ์วิธี
สอนแนวคิดแยกซ้าย/ขวาprefix + suffix อาร์เรย์
ข้อสอบที่จำกัดพื้นที่เสริมสองผ่านบนคำตอบ
มีสิทธิ์หารและไม่มีศูนย์ยังไม่ใช้ — ผิดสเปกมาตรฐาน
สตรีมครั้งเดียวโดยไม่เก็บอาร์เรย์โจทย์นี้ไม่รองรับโดยตรง

Recap

  • แยกผลคูณซ้ายและขวาของแต่ละดัชนี
  • ระวังศูนย์หนึ่งตัวกับหลายตัว
  • อย่าใช้หารแม้ดูสั้นกว่า
  • เทียบสองอิมพลีเมนต์บนเคสที่มีศูนย์ก่อนส่ง