ลา แม่เป็ดกับลูกเป็ด หมาบอร์เดอร์คอลลี่ ปลาหมึก และบีเวอร์การ์ตูนแย่งกันดันลังเปล่าไปท้ายชั้นวางลังแอปเปิล มีสิงโตทะเลใส่โค้ทกรมท่ากอดอกยืนดูอยู่ด้านขวา

LeetCode #283 Move Zeroes: 5 สัตว์เถียงกันว่าใครย้ายลังเปล่าได้เร็วสุด

น้องกายEngineering

นึกภาพชั้นวางลังที่แผงผลไม้ครับ บางลังมีแอปเปิลเต็ม บางลังว่างเปล่า เจ้าของแผงอยากให้ลังว่างไปกองอยู่ท้ายชั้นทั้งหมด แต่ลังที่มีของต้องเรียงลำดับเหมือนเดิม เพราะลูกค้าประจำจำได้ว่าลังไหนอยู่ก่อนอยู่หลัง

นั่นแหละคือ Move Zeroes #283 เขาให้ list ตัวเลข nums มา ให้ย้ายเลข 0 ทุกตัวไปไว้ท้าย โดยเลขที่ไม่ใช่ศูนย์ต้องเรียงตามลำดับเดิม และต้องแก้ใน list เดิมเลย (in-place) ห้ามทำสำเนา ฟังก์ชันไม่ต้อง return อะไร เช่น [0, 1, 0, 3, 12] ต้องกลายเป็น [1, 3, 12, 0, 0] โจทย์กำหนดความยาว 1 ถึง 10,000 ตัว

ข้อนี้ดูเหมือนข้อง่ายๆ แต่ทำได้หลายแบบมาก และแต่ละแบบเร็วช้าต่างกันแบบเห็นชัด รอบนี้ผมเลยให้สัตว์ 5 ตัวมาเถียงกันว่าใครเก่งสุด แล้วเอา benchmark ที่วัดจริงมาตัดสิน ปิดท้ายด้วยบารอนสรุปให้เหมือนเดิมครับ

รู้จักผู้เข้าแข่งขัน

ตัวละครวิธีนิสัย
ลุงลา ลาแก่ใส่หมวกฟาง ผ้าพันคอแดง แบกลังไว้บนหลังลบศูนย์แล้วไปต่อท้ายลาขนของทีละเที่ยว ขยัน ดื้อ ไม่ยอมเปลี่ยนวิธี
แม่เป็ด เป็ดขาวโพกผ้าสีเทาอมเขียว คล้องนกหวีดสร้าง list ใหม่พาลูกเป็ดเดินแถวใหม่ให้เป๊ะ ไม่สนแถวเก่า
ตูบต้อน หมาบอร์เดอร์คอลลี่ ผ้าพันคอมิ้นต์ เสื้อกั๊กเขียวขี้ม้าsort ด้วย keyหมาต้อนแกะ แยกฝูงเป็นสองกองเก่งมาก
ป้าหมึก ปลาหมึกสีชมพูม่วง เสื้อไหมพรมครีม แว่นกลมสองตัวชี้สลับที่ (two pointers)มีหลายมือ จับสองลังสลับกันได้ในที่เดียว
น้าบีเวอร์ บีเวอร์ใส่หมวกนิรภัยเหลือง คาดเข็มขัดเครื่องมือ ถือค้อนไม้เขียนทับแล้วเติม 0ช่างสร้างเขื่อน วางของแน่นๆ จากซ้ายแล้วอุดท้ายให้เรียบร้อย
บารอน สิงโตทะเลใส่โค้ทกรมท่า เสื้อคอเต่าสีเทาอมเขียวกรรมการเข้มแต่แฟร์ ฟังตัวเลขมากกว่าเสียงดัง

ตัวละครการ์ตูนหกตัวยืนเรียงกัน จากซ้าย ลาแก่แบกลังใส่หมวกฟาง แม่เป็ดโพกผ้ากับลูกเป็ดสามตัว หมาบอร์เดอร์คอลลี่ ปลาหมึกสีชมพูใส่แว่นถือลังสองใบ บีเวอร์ใส่หมวกนิรภัย และสิงโตทะเลใส่โค้ทกรมท่ากอดอก

ยกที่ 1: ลุงลา ขนทีละเที่ยว

ลาแก่แบกลังเปล่าหนึ่งใบเดินไปตามชั้นวางลังที่ยาวมาก พื้นมีรอยกีบเท้าเดินไปกลับหลายรอบ

ลุงลา: “ไม่เห็นยาก เจอลังเปล่าก็ยกออกมา แล้วแบกไปวางท้ายแถว ทำไปเรื่อยๆ จนหมด จบ”

แม่เป็ด: “ลุงรู้ตัวไหมว่ายกออกทีนึง ลังข้างหลังทั้งแถวต้องขยับมาเติมช่องว่างทุกรอบเลยนะ”

ลุงลา: “ก็ให้มันขยับไปสิ ลุงไม่ได้ขยับเองซะหน่อย”

แม่เป็ด: “แต่คอมมันต้องขยับให้ไงลุง ศูนย์สามพันตัวก็ขยับสามพันรอบ”

ใน Python nums.remove(0) ต้องหาเลข 0 ตัวแรก แล้วเลื่อนทุกตัวที่อยู่ข้างหลังขึ้นมาหนึ่งช่อง (Python wiki ระบุว่าการลบจาก list เป็น O(n)) ถ้ามีศูนย์ z ตัว ก็ทำแบบนี้ z รอบ รวมเป็น O(z·n) แย่สุดคือ O(n²) ตอนศูนย์เยอะๆ ข้อดีคือไม่ใช้หน่วยความจำเพิ่มเลย

  • ข้อดี: เขียนสามบรรทัด อ่านแล้วเข้าใจทันที
  • ข้อเสีย: ช้าลงแบบกำลังสองเมื่อศูนย์เยอะ

ยกที่ 2: แม่เป็ด ตั้งแถวใหม่

แม่เป็ดโพกผ้าพาลูกเป็ดเดินเรียงแถว ลูกเป็ดแต่ละตัวถือลังแอปเปิลย้ายไปชั้นวางใหม่ตามลำดับเดิม ลังเปล่ากองรอไว้ปลายชั้น

แม่เป็ด: “ทำไมต้องยกเข้ายกออกให้วุ่นวาย แม่เป็ดตั้งแถวใหม่เลย ลูกๆ ถือลังที่มีแอปเปิลเดินตามแม่ไปทีละตัว เสร็จแล้วค่อยเติมลังเปล่าต่อท้าย เรียบร้อย”

ป้าหมึก: “แต่โจทย์เขาบอกว่าห้ามทำสำเนานะแม่เป็ด”

แม่เป็ด: “แม่ก็เอาแถวใหม่ไปแปะทับแถวเก่าไง list เดิมก็เปลี่ยนจริงนะ”

ป้าหมึก: “ผลออกมาถูกก็จริง แต่ระหว่างทางแม่ใช้ชั้นวางเพิ่มอีกชุดนึงนะ”

เราสร้าง list ใหม่ที่มีแต่ตัวที่ไม่ใช่ศูนย์ แล้วเอาไปเขียนทับ list เดิมด้วย nums[:] = ... ทำให้คนที่ถือ list เดิมอยู่เห็นผลด้วย (ต่างจาก nums = ... ที่แค่ผูกชื่อใหม่ แล้ว list ของคนเรียกไม่เปลี่ยน) เวลา O(n) แต่ใช้ที่เพิ่ม O(n) ซึ่งขัดกับเงื่อนไข “ห้ามทำสำเนา” ของโจทย์

  • ข้อดี: โค้ดสั้น อ่านง่ายมาก และใน Python ทำงานเร็ว เพราะ list comprehension เป็นลูปที่ Python ปรับมาให้เร็วเป็นพิเศษ ส่วนการต่อ list กับ nums[:] = ทำในโค้ด C ทั้งก้อน
  • ข้อเสีย: เปลืองหน่วยความจำเท่าข้อมูลทั้งชุด และไม่ตรงเงื่อนไขของโจทย์

ยกที่ 3: ตูบต้อน แยกฝูง

หมาบอร์เดอร์คอลลี่หมอบต้อนลังแอปเปิลไปทางซ้ายและลังเปล่าไปทางขวา มีลูกศรโค้งบอกทิศทาง

ตูบต้อน: “โฮ่ง! เรื่องแยกฝูงต้องยกให้ผม บอกทุกลังว่า ‘ลังเปล่าไปทางขวา ลังมีของไปทางซ้าย’ แล้วให้ sort จัดการ”

น้าบีเวอร์: “เดี๋ยว sort มันจะไปสลับลำดับลังแอปเปิลหรือเปล่า”

ตูบต้อน: “ไม่สลับครับน้า sort ของ Python เป็นแบบ stable ตัวที่ key เท่ากันจะอยู่ลำดับเดิม”

nums.sort(key=lambda x: x == 0) ให้ key เป็น False กับตัวที่ไม่ใช่ศูนย์ และ True กับศูนย์ เพราะ False น้อยกว่า True ศูนย์จึงไปอยู่ท้าย และเพราะ sort ของ Python เป็น stable ลำดับของตัวที่ไม่ใช่ศูนย์จึงไม่เปลี่ยน เวลา O(n log n) และ Python ต้องสร้าง key เก็บไว้ทุกตัว เลยใช้ที่เพิ่ม O(n)

  • ข้อดี: บรรทัดเดียว และโชว์ว่าเข้าใจเรื่อง stable sort
  • ข้อเสีย: ใช้ sort ทั้งที่จริงๆ แค่ต้องแยกเป็นสองกอง เลยทำงานเกินจำเป็น

ยกที่ 4: ป้าหมึก สลับในที่

ปลาหมึกสีชมพูใส่แว่นยืนหน้าชั้นวาง ใช้หนวดสองเส้นสลับลังแอปเปิลกับลังเปล่าในชั้นเดียวกัน

ป้าหมึก: “ป้าไม่ต้องใช้ชั้นวางเพิ่มนะจ๊ะ ป้าใช้หนวดสองเส้น เส้นนึงชี้ช่องที่ลังมีของควรจะไปอยู่ อีกเส้นเดินสำรวจไปข้างหน้า เจอลังมีของเมื่อไหร่ก็สลับกับช่องที่ชี้ไว้ แล้วขยับเส้นแรกไปหนึ่งช่อง”

ลุงลา: “หนวดเยอะก็ได้เปรียบสิ”

ป้าหมึก: “ใช้แค่สองเส้นเองลุง ที่เหลือเอาไว้ถือกาแฟ”

นี่คือเทคนิค two pointers ตัวชี้ slow บอกช่องถัดไปที่ตัวไม่ใช่ศูนย์ควรไปอยู่ ส่วน fast เดินดูทุกตัว เจอตัวที่ไม่ใช่ศูนย์ก็สลับ nums[slow] กับ nums[fast] แล้วเลื่อน slow ไปหนึ่งช่อง เดินรอบเดียว O(n) ใช้ที่เพิ่ม O(1) เป็นวิธีที่ตรงเงื่อนไข in-place ที่สุด

  • ข้อดี: รอบเดียว ไม่ใช้ที่เพิ่ม ศูนย์ไปกองท้ายเองไม่ต้องเติม
  • ข้อเสีย: ต้องอธิบายให้ชัดว่าตัวชี้แต่ละตัวหมายถึงอะไร ไม่งั้นคนฟังงง

ยกที่ 5: น้าบีเวอร์ วางแน่นแล้วอุดท้าย

บีเวอร์ใส่หมวกนิรภัยเหลืองยิ้มกว้าง ใช้ค้อนไม้เคาะลังเปล่าเข้าที่ท้ายชั้นวาง ส่วนหัวชั้นมีลังแอปเปิลเรียงแน่น

น้าบีเวอร์: “หมึกสลับไปสลับมาก็เหนื่อยเปล่า น้าเอาลังมีของมาวางทับจากซ้ายไปเรื่อยๆ เหมือนวางไม้ทำเขื่อน วางหมดแล้วช่องที่เหลือท้ายแถว ก็เคาะลังเปล่าอุดให้หมด เสร็จ”

ป้าหมึก: “แต่น้าต้องเดินสองรอบนะ”

น้าบีเวอร์: “รอบสองมันแค่ช่องท้ายๆ เองป้า ไม่ได้เดินทั้งแถว”

รอบแรกเดินดูทุกตัว ตัวไหนไม่ใช่ศูนย์ก็เขียนลงช่อง write แล้วเลื่อน write ไป รอบสองเติม 0 ตั้งแต่ write จนสุด list เวลา O(n) ที่เพิ่ม O(1) ไม่ต้องสลับ แค่เขียนทับ

  • ข้อดี: in-place ไม่มีการสลับ แต่ละรอบทำงานแค่อย่างเดียว
  • ข้อเสีย: เขียนลงช่องที่ต้องกลายเป็นศูนย์ซ้ำอีกรอบ ถ้าคนสัมภาษณ์ถามเรื่องลดจำนวนครั้งที่เขียน ต้องคุยเรื่องนี้

โค้ดทั้ง 5 วิธีพร้อม test

บล็อกนี้รันจริงตอนตรวจบทความ ทดสอบทุกวิธีกับตัวอย่างโจทย์ กรณีขอบ (ตัวเดียว, ศูนย์ล้วน, ไม่มีศูนย์, ศูนย์อยู่หน้าหรือท้าย, เลขติดลบ) เช็กว่าทุกวิธี return None และแก้ list เดิมจริง (ตัวแปรอีกชื่อที่ชี้ list เดียวกันต้องเห็นผล) แล้วสุ่มอีก 300 ชุดเทียบกับคำตอบอ้างอิง

import random
import unittest


def move_remove_append(nums: list[int]) -> None:
    for _ in range(nums.count(0)):
        nums.remove(0)
        nums.append(0)


def move_new_list(nums: list[int]) -> None:
    kept = [x for x in nums if x != 0]
    nums[:] = kept + [0] * (len(nums) - len(kept))


def move_sort_key(nums: list[int]) -> None:
    nums.sort(key=lambda x: x == 0)


def move_swap(nums: list[int]) -> None:
    slow = 0
    for fast in range(len(nums)):
        if nums[fast] != 0:
            nums[slow], nums[fast] = nums[fast], nums[slow]
            slow += 1


def move_overwrite_fill(nums: list[int]) -> None:
    write = 0
    for x in nums:
        if x != 0:
            nums[write] = x
            write += 1
    for i in range(write, len(nums)):
        nums[i] = 0


ALL = [move_remove_append, move_new_list, move_sort_key, move_swap, move_overwrite_fill]


def expected(nums: list[int]) -> list[int]:
    kept = [x for x in nums if x != 0]
    return kept + [0] * (len(nums) - len(kept))


class TestMoveZeroes(unittest.TestCase):
    def check(self, nums: list[int]) -> None:
        for fn in ALL:
            with self.subTest(fn=fn.__name__, nums=nums[:10]):
                data = list(nums)
                original_id = id(data)
                self.assertIsNone(fn(data))
                self.assertEqual(id(data), original_id)
                self.assertEqual(data, expected(nums))

    def test_examples(self) -> None:
        self.check([0, 1, 0, 3, 12])
        self.check([0])

    def test_edges(self) -> None:
        self.check([1])
        self.check([0, 0, 0])
        self.check([1, 2, 3])
        self.check([0, 0, 1])
        self.check([1, 0, 0])
        self.check([-1, 0, -2, 0, 5])

    def test_alias_sees_change(self) -> None:
        for fn in ALL:
            data = [0, 4, 0, 2]
            alias = data
            fn(data)
            self.assertEqual(alias, [4, 2, 0, 0])

    def test_random_against_each_other(self) -> None:
        rng = random.Random(283)
        for _ in range(300):
            n = rng.randint(1, 50)
            nums = [rng.choice([0, 0, rng.randint(-9, 9)]) for _ in range(n)]
            self.check(nums)


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

test_alias_sees_change มีไว้จับบั๊กยอดฮิตครับ ถ้าใครเขียน nums = [...] แทน nums[:] = [...] ตัว list ที่คนเรียกถืออยู่จะไม่เปลี่ยน test นี้จะพังทันที

benchmark ตัดสิน

ลา แม่เป็ด หมาบอร์เดอร์คอลลี่ ปลาหมึก และบีเวอร์เถียงกันวุ่นรอบชั้นวางลัง สิงโตทะเลใส่โค้ทกรมท่าเดินเข้ามายกมือห้าม

บารอน: “เอาล่ะ หยุดก่อน เถียงกันไปก็ไม่จบ ใครเร็วจริงไปวัดมาให้ดู”

ผมวัดบน box ของบล็อก (Intel Xeon 8 vCPU, Linux, Python 3.14.7) ด้วย timeit แต่ละครั้งเริ่มจากสำเนาใหม่ของ list เดียวกัน (สุ่มด้วย seed 283 ให้มีศูนย์ราว 30%) เพราะทุกวิธีแก้ list เดิม ถ้าไม่ทำสำเนา รอบถัดไปจะได้ list ที่จัดเสร็จแล้ว เวลาที่รายงานคือค่า median ต่อการเรียกหนึ่งครั้ง และไม่นับเวลาทำสำเนา

ขนาดที่วัดมีสองแบบ แบบแรกคือ n = 10,000 ซึ่งใหญ่สุดตามโจทย์ (มีศูนย์ 3,003 ตัว) อีกแบบคือ n = 1,000,000 ที่ใหญ่เกินโจทย์ (ศูนย์ 299,353 ตัว) เอาไว้ดูว่าพอข้อมูลโตแล้วอันดับเปลี่ยนไหม ลุงลาวัดแค่ขนาดแรก เพราะเป็น O(n²)

โค้ดด้านล่างเป็นโค้ดบางส่วน ใช้ฟังก์ชันจากบล็อกด้านบนที่ผมเซฟเป็นไฟล์ solutions.py

import random, statistics, timeit
from solutions import ALL

def make(n, seed=283):
    rng = random.Random(seed)
    return [0 if rng.random() < 0.3 else rng.randint(1, 1000) for _ in range(n)]

for n, fns, rep in [(10_000, ALL, 15), (1_000_000, ALL[1:], 7)]:
    base = make(n)
    for fn in fns:
        g = {"fn": fn, "base": base}
        times = timeit.repeat("fn(data)", setup="data = list(base)",
                              globals=g, number=1, repeat=rep)
        print(n, fn.__name__, f"{statistics.median(times) * 1000:.3f} ms")

กราฟแท่งสเกล log เวลาต่อการเรียกของทั้ง 5 วิธี ที่ n เท่ากับ 10,000 ลุงลา 89.4 ms แม่เป็ด 0.319 ms ตูบต้อน 0.615 ms ป้าหมึก 0.466 ms น้าบีเวอร์ 0.412 ms และที่ n เท่ากับ 1,000,000 แม่เป็ด 46.3 ms ตูบต้อน 76.4 ms ป้าหมึก 41.5 ms น้าบีเวอร์ 40.2 ms

วิธีn = 10,000n = 1,000,000
ลุงลา (ลบแล้วต่อท้าย)89.416 msไม่ได้วัด
แม่เป็ด (list ใหม่)0.319 ms46.331 ms
ตูบต้อน (sort ด้วย key)0.615 ms76.449 ms
ป้าหมึก (สองตัวชี้สลับ)0.466 ms41.492 ms
น้าบีเวอร์ (เขียนทับแล้วเติม 0)0.412 ms40.194 ms

ผมรันชุดนี้ซ้ำอีกรอบ ตัวเลขขยับไปบ้าง แต่อันดับเหมือนเดิมทั้งสองขนาด ตัวเลขทั้งหมดมาจากเครื่องเดียว เครื่องอื่นหรือ Python เวอร์ชันอื่นจะได้ค่าไม่เท่านี้ สิ่งที่เห็นจากการวัดคือ

  • ที่ n = 10,000 ลุงลาช้ากว่าแม่เป็ดราว 280 เท่า (89.416 ÷ 0.319) นี่คือราคาของการเลื่อนทั้งแถวทุกครั้งที่ลบศูนย์
  • ที่ขนาดตามโจทย์ แม่เป็ดเร็วสุด เพราะ list comprehension เป็นลูปที่ Python ปรับให้เร็วเป็นพิเศษ และการต่อ list กับเขียนทับทั้งก้อนทำในโค้ด C ส่วนป้าหมึกกับน้าบีเวอร์ต้องอ่านเขียน list ทีละช่องผ่าน index
  • พอขยายเป็นล้านตัว น้าบีเวอร์กับป้าหมึกแซงแม่เป็ด แต่ชนะกันไม่มาก (40.2 กับ 41.5 กับ 46.3 ms) ผมเดาว่าแม่เป็ดเสียเวลาไปกับการสร้าง list ใหม่ขนาดใหญ่หลายก้อน (ตัวที่ไม่ใช่ศูนย์, ศูนย์, และตัวที่ต่อกันแล้ว) แต่ข้อนี้ไม่ได้วัดแยกให้เห็น
  • ตูบต้อนช้ากว่ากลุ่ม O(n) ทั้งสองขนาด เพราะใช้ sort ทำงานที่แค่แยกสองกองก็พอ
  • Big-O บอกว่าโตเร็วแค่ไหน แต่ใครชนะในภาษาจริงต้องวัดเอง

แม่เป็ด: “เห็นไหม ขนาดตามโจทย์แม่ชนะนะ”

น้าบีเวอร์: “แต่ล้านตัวน้าชนะนะ แถมไม่ต้องใช้ชั้นวางเพิ่มด้วย”

ลุงลา: “ลุงขอแบกลังกลับบ้านก่อนละกัน”

บารอนสรุป

สิงโตทะเลใส่โค้ทกรมท่ายืนอธิบายหน้ากระดานกราฟแท่ง บีเวอร์ ปลาหมึก แม่เป็ดกับลูกเป็ด และหมาบอร์เดอร์คอลลี่ยืนฟัง ลาแก่ลดหูลงอย่างเขินๆ

บารอน: “ฟังนะ ทุกคนได้คำตอบถูกหมด แต่โจทย์นี้ไม่ได้วัดกันที่ความเร็วอย่างเดียว เขาสั่งว่าต้องแก้ในที่ ห้ามทำสำเนา

ลุงลาเก็บไว้เทียบคำตอบตอนเขียน test พอ ใช้จริงไม่ไหว แม่เป็ดเร็วจริงที่ขนาดโจทย์ แต่ใช้ชั้นวางเพิ่ม เลยผิดเงื่อนไข เอาไปใช้ตอนเขียนสคริปต์ทั่วไปที่ไม่ห่วงหน่วยความจำได้ ตูบต้อนบรรทัดเดียวน่ารักดี แต่ใช้ sort โดยไม่จำเป็น

ถ้าสัมภาษณ์ ตอบป้าหมึกหรือน้าบีเวอร์ ทั้งคู่รอบเดียว ไม่ใช้ที่เพิ่ม ถ้าเขาถามต่อว่าลดจำนวนครั้งที่เขียนลง list ได้ไหม ให้เล่าว่าน้าบีเวอร์เขียนลง list ทุกช่องรวม n ครั้งพอดี ส่วนป้าหมึกสลับทุกครั้งที่เจอตัวที่ไม่ใช่ศูนย์ ครั้งละสองช่อง แม้ตอนที่สองตัวชี้ชี้ช่องเดียวกัน ถ้าให้ป้าหมึกข้ามการสลับตอน slow เท่ากับ fast ก็ลดการเขียนที่ไม่จำเป็นลงได้

สรุปสั้นๆ เริ่มจากลุงลาให้เห็นว่าเข้าใจโจทย์ ชี้ว่าแม่เป็ดผิดเงื่อนไขตรงไหน แล้วจบที่ป้าหมึกหรือน้าบีเวอร์ แค่นี้ผ่าน”

สรุปเทียบทุกวิธี

วิธีเวลาที่เพิ่มตรงเงื่อนไข in-placeใช้เมื่อ
ลุงลา (ลบแล้วต่อท้าย)O(z·n) แย่สุด O(n²)O(1)✅เป็นตัวเทียบคำตอบใน test
แม่เป็ด (list ใหม่)O(n)O(n)❌ ทำสำเนาสคริปต์ทั่วไปที่ไม่ห่วงหน่วยความจำ
ตูบต้อน (sort ด้วย key)O(n log n)O(n) สำหรับ key⚠️ แก้ list เดิม แต่ใช้ที่เพิ่มอยากได้บรรทัดเดียว
ป้าหมึก (สองตัวชี้สลับ)O(n)O(1)✅คำตอบหลักตอนสัมภาษณ์
น้าบีเวอร์ (เขียนทับแล้วเติม 0)O(n)O(1)✅คำตอบหลักตอนสัมภาษณ์ เร็วสุดที่ล้านตัวในการวัดนี้

จุดที่พลาดกันบ่อย

  • เขียน nums = ... แทน nums[:] = ... แบบแรกแค่ผูกชื่อ nums ใน function กับ list ใหม่ list ของคนเรียกไม่เปลี่ยนเลย
  • ลบสมาชิกระหว่างวนลูป for x in nums ตำแหน่งจะเลื่อนจนข้ามบางตัวไป ถ้าจะลบให้ทำแบบลุงลาที่นับจำนวนศูนย์ก่อน
  • ลืมว่าเลขติดลบไม่ใช่ศูนย์ เงื่อนไขต้องเป็น x != 0 ไม่ใช่ x > 0
  • ใช้ sort ธรรมดาโดยไม่มี key nums.sort() จะเรียงตัวเลขด้วย ลำดับเดิมหายหมด
  • return list ออกไป โจทย์ไม่ได้ให้ return อะไร ระบบตรวจจะดูจาก list เดิม

ลองต่อเอง

ลองเปลี่ยนโจทย์เป็น “ย้ายเลขคู่ไปหน้า เลขคี่ไปหลัง” แล้วดูว่าใครในห้าตัวนี้ยังเอาวิธีเดิมไปใช้ได้ ป้าหมึกปรับนิดเดียวก็ได้ ตูบต้อนแค่เปลี่ยน key ส่วนน้าบีเวอร์ต้องคิดใหม่ว่าจะเติมอะไรท้ายแถว ข้อนี้คือ Sort Array By Parity #905 ครับ