0Pricing
Coding Interview Prep · บทเรียน

เกมกระโดด I และ II

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

เกมกระโดด I และ II เป็นบทเรียน Coding Interview Prep ฟรีบน CoddyKit นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน คุณสามารถอ่านบทเรียนทั้งหมดด้านล่างฟรี — จากนั้นลองปฏิบัติด้วยตัวคุณเองในเบราว์เซอร์พร้อมตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7 บทเรียนนี้เป็นส่วนหนึ่งของเส้นทางการเรียน Coding Interview Prep และความก้าวหน้าของคุณจะซิงค์ข้ามเว็บและแอป CoddyKit คอร์ส Coding Interview Prep มีบทเรียนทั้งหมด 4 บทเรียน

เกมกระโดด I: เข้าถึงจุดสิ้นสุดได้หรือไม่

เกมกระโดด I (LeetCode 55): กำหนดอาร์เรย์ที่ nums[i] คือความยาวการกระโดดสูงสุดจากดัชนี i ให้พิจารณาว่าคุณสามารถเข้าถึงดัชนีสุดท้ายโดยเริ่มจากดัชนี 0 ได้หรือไม่ สำหรับ [2, 3, 1, 1, 4] คุณสามารถไปถึงจุดสิ้นสุดได้ (jump 2→3 จากนั้น 3 ก็พาไปถึงจุดสิ้นสุด) สำหรับ [3, 2, 1, 0, 4] คุณไปถึงไม่ได้ (จะลงที่ 0 เสมอ และ 0 มีค่าการกระโดดเป็น 0) วิธีแบบละโมบใช้เวลา O(n)

# Can you reach the last index?
nums1 = [2, 3, 1, 1, 4]  # True: 0→1→4 or 0→2→3→4
nums2 = [3, 2, 1, 0, 4]  # False: always land on index 3 (value 0)

# At index 3 (value 0): no matter how you get here,
# you can't jump further to reach index 4
print('nums1 last index:', len(nums1)-1)
print('nums2 index 3 jump value:', nums2[3])  # 0 = stuck

วิธีแบบละโมบ: ติดตามระยะที่เข้าถึงได้สูงสุด

แนวคิดแบบละโมบสำหรับเกมกระโดด I คือ รักษาค่า max_reach ซึ่งเป็นดัชนีที่ไกลที่สุดที่เข้าถึงได้จนถึงขณะนั้น ที่แต่ละดัชนี i ให้อัปเดต max_reach = max(max_reach, i + nums[i]) หากเมื่อใดก็ตาม i > max_reach ดัชนีปัจจุบันจะเข้าถึงไม่ได้ — ให้คืนค่าเท็จ หากเราไปถึงหรือเลยดัชนีสุดท้าย ให้คืนค่าจริง ไม่จำเป็นต้องใช้ DP หรือการย้อนกลับ

def can_jump(nums):
    max_reach = 0
    for i, jump in enumerate(nums):
        if i > max_reach:      # can't reach index i
            return False
        max_reach = max(max_reach, i + jump)
        if max_reach >= len(nums) - 1:
            return True  # early exit
    return True

print(can_jump([2, 3, 1, 1, 4]))  # True
print(can_jump([3, 2, 1, 0, 4]))  # False
print(can_jump([0]))              # True (already at last index)
print(can_jump([1, 0, 0]))        # False

การติดตามเกมกระโดด I

ติดตาม [3, 2, 1, 0, 4]: i=0, jump=3, max_reach=3. i=1, jump=2, max_reach=max(3,3)=3. i=2, jump=1, max_reach=max(3,3)=3. i=3, jump=0, max_reach=max(3,3)=3. i=4, i=4 > max_reach=3 → คืนค่าเท็จ อัลกอริทึมระบุได้อย่างถูกต้องว่าดัชนี 4 เข้าถึงไม่ได้ ทุกเส้นทางจากดัชนี 0 ติดอยู่ เพราะค่า 0 ที่ดัชนี 3 จำกัด max_reach ไว้ที่ 3

def can_jump_trace(nums):
    max_reach = 0
    for i, jump in enumerate(nums):
        print(f'i={i}, jump={jump}, max_reach before={max_reach}', end='')
        if i > max_reach:
            print(' → UNREACHABLE')
            return False
        max_reach = max(max_reach, i + jump)
        print(f' → max_reach={max_reach}')
    return True

print('Result:', can_jump_trace([3, 2, 1, 0, 4]))

เกมกระโดด II: จำนวนการกระโดดน้อยที่สุด

เกมกระโดด II (LeetCode 45) ถามหาจำนวน การกระโดดน้อยที่สุดเพื่อไปถึงดัชนีสุดท้าย (รับประกันว่าไปถึงได้เสมอ) วิธีแบบละโมบใช้กลยุทธ์ การขยายช่วง: รักษาระยะที่ไกลที่สุดของการกระโดดปัจจุบัน (curr_end) และระยะที่ไกลที่สุดของการกระโดดครั้งถัดไป (farthest) เมื่อใช้ช่วงของการกระโดดปัจจุบันจนหมด คุณต้องกระโดด — เพิ่มค่า jumps และกำหนด curr_end = farthest

def jump(nums):
    n = len(nums)
    if n == 1: return 0  # already at destination
    jumps = 0
    curr_end = 0   # end of current jump's range
    farthest = 0   # farthest reachable in next jump
    for i in range(n - 1):  # don't jump from last index
        farthest = max(farthest, i + nums[i])
        if i == curr_end:    # exhausted current jump range
            jumps += 1
            curr_end = farthest
            if curr_end >= n - 1: break
    return jumps

print(jump([2, 3, 1, 1, 4]))  # 2 (0→1→4)
print(jump([2, 3, 0, 1, 4]))  # 2 (0→1→4)
print(jump([1, 2, 1, 1, 1])) # 3

การแสดงภาพเกมกระโดด II

ให้มองเกมกระโดด II เป็นวิธีแบบ BFS ทีละระดับ โดยไม่ต้องเสียค่าใช้จ่ายเพิ่มเติมจากคิว การกระโดดแต่ละครั้งสอดคล้องกับหนึ่งระดับของ BFS curr_end คือขอบเขตของระดับปัจจุบัน ส่วน farthest คือดัชนีสูงสุดที่เข้าถึงได้ในระดับถัดไป เมื่อคุณตรวจสอบระดับปัจจุบันเสร็จ (i == curr_end) คุณจะทราบขอบเขตของระดับถัดไปแล้ว และต้องเพิ่มจำนวนการกระโดด นี่คือ BFS บนกราฟโดยนัยที่ใช้เวลา O(n) และพื้นที่ O(1)

def jump_traced(nums):
    n = len(nums)
    jumps = curr_end = farthest = 0
    for i in range(n - 1):
        farthest = max(farthest, i + nums[i])
        print(f'i={i}: farthest={farthest}, curr_end={curr_end}')
        if i == curr_end:
            jumps += 1
            curr_end = farthest
            print(f'  → JUMP #{jumps}, new range ends at {curr_end}')
            if curr_end >= n - 1: break
    return jumps

print('Min jumps:', jump_traced([2, 3, 1, 1, 4]))

เหตุใดวิธีแบบละโมบจึงถูกต้องสำหรับเกมกระโดด II

เหตุใดวิธีแบบละโมบ (ขยายไปยังจุดที่ไกลที่สุดเสมอ) จึงให้จำนวนการกระโดดน้อยที่สุด การพิสูจน์ด้วยการสลับ: สมมติว่าคำตอบที่เหมาะสมที่สุดกระโดดไปไม่ถึงจุดที่ไกลที่สุด เราสามารถขยายการกระโดดนั้นให้ไปถึง farthest ได้เสมอโดยไม่มีต้นทุนเพิ่มเติม — ยังคงเป็นการกระโดดเพียงครั้งเดียว การเลือกช่วงที่กว้างที่สุดในการกระโดดแต่ละครั้งทำให้มั่นใจได้ว่าจะใช้จำนวนการกระโดดน้อยที่สุด วิธีใดก็ตามที่ใช้ช่วงน้อยกว่าต่อการกระโดดหนึ่งครั้งจะทำได้ไม่ดีกว่า และจะต้องใช้การกระโดดมากขึ้นเพื่อครอบคลุมระยะทางเดียวกัน

# Correctness verification: compare to BFS
from collections import deque

def jump_bfs(nums):
    n = len(nums)
    if n == 1: return 0
    visited = [False] * n
    visited[0] = True
    queue = deque([0])
    level = 0
    while queue:
        level += 1
        for _ in range(len(queue)):
            pos = queue.popleft()
            for j in range(1, nums[pos] + 1):
                nxt = pos + j
                if nxt >= n - 1: return level
                if not visited[nxt]:
                    visited[nxt] = True
                    queue.append(nxt)
    return -1

# Both should give same results
for nums in [[2,3,1,1,4],[2,3,0,1,4],[1,2,1,1,1]]:
    print(jump(nums), '==', jump_bfs(nums))

ทางเลือก DP สำหรับเกมกระโดด II

วิธีแก้ด้วย DP: dp[i] = จำนวนการกระโดดน้อยที่สุดเพื่อไปถึงดัชนี i สำหรับแต่ละตำแหน่ง j ให้อัปเดตตำแหน่งทั้งหมดที่เข้าถึงได้: dp[j+k] = min(dp[j+k], dp[j]+1) สำหรับ k ใน 1..nums[j] วิธีนี้ใช้เวลา O(n × ค่าการกระโดดสูงสุด) และพื้นที่ O(n) — ช้ากว่าวิธีแบบละโมบที่ใช้เวลา O(n) มาก วิธีแบบละโมบจึงเหนือกว่าในกรณีนี้ ส่วน DP แสดงไว้เพื่อเปรียบเทียบและแสดงให้เห็นว่าวิธีแบบละโมบสามารถหลีกเลี่ยงลูปด้านในได้อย่างไร

def jump_dp(nums):
    n = len(nums)
    dp = [float('inf')] * n
    dp[0] = 0
    for j in range(n):
        for k in range(1, nums[j] + 1):
            if j + k < n:
                dp[j+k] = min(dp[j+k], dp[j] + 1)
    return dp[n-1]

print(jump_dp([2, 3, 1, 1, 4]))  # 2
print(jump_dp([1, 2, 1, 1, 1]))  # 3

# Greedy is O(n), DP is O(n * max_jump)
# For large inputs with big jump values, greedy is much faster

เกมกระโดด III: เข้าถึงดัชนีศูนย์

เกมกระโดด III (LeetCode 1306): เริ่มจากดัชนีที่กำหนด จากดัชนี i ให้ jump ไปยัง i + nums[i] หรือ i - nums[i] คุณสามารถเข้าถึงดัชนีใด ๆ ที่มีค่าเป็น 0 ได้หรือไม่ นี่เป็นปัญหาการเข้าถึง (BFS/DFS) ไม่ใช่ปัญหาการหาค่าต่ำสุด จึงใช้วิธีแบบละโมบไม่ได้ ให้ใช้ BFS ร่วมกับเซตของตำแหน่งที่เยี่ยมชมแล้วเพื่อป้องกันวัฏจักร เวลา: O(n)

from collections import deque

def can_reach(arr, start):
    n = len(arr)
    visited = set()
    queue = deque([start])
    while queue:
        idx = queue.popleft()
        if arr[idx] == 0: return True
        if idx in visited: continue
        visited.add(idx)
        for nxt in [idx + arr[idx], idx - arr[idx]]:
            if 0 <= nxt < n and nxt not in visited:
                queue.append(nxt)
    return False

print(can_reach([4,2,3,0,3,1,2], 5))  # True (5→4→1→3, arr[3]=0)
print(can_reach([3,0,2,1,2], 2))      # False (can't reach index 1, arr[1]=0)

เกมกระโดด VII: เข้าถึงได้ด้วยช่วง

เกมกระโดด VII (LeetCode 1871): คุณสามารถเดินทางผ่านสตริงไบนารีโดยกระโดดจากดัชนี 0 ไปยังดัชนีสุดท้ายได้หรือไม่ โดยจากตำแหน่ง i คุณสามารถกระโดดไปยัง '0' ใด ๆ ในช่วง [i+minJump, i+maxJump] ให้ใช้ผลรวมหน้าต่างเลื่อนบนอาร์เรย์ของตำแหน่งที่เข้าถึงได้ รักษาผลรวมสะสมของตำแหน่งที่เข้าถึงได้ไว้ ตำแหน่ง j จะเข้าถึงได้หากมีตำแหน่งที่เข้าถึงได้อยู่ในช่วง [j-maxJump, j-minJump]

def can_reach_vii(s, min_jump, max_jump):
    n = len(s)
    reach = [False] * n
    reach[0] = True
    pre = [0] * (n + 1)  # prefix sum of reachable positions
    pre[1] = 1
    for j in range(1, n):
        # Window sum: any reachable position in [j-maxJump, j-minJump]?
        lo = max(0, j - max_jump)
        hi = max(0, j - min_jump + 1)
        window_sum = pre[hi] - pre[lo]
        if s[j] == '0' and window_sum > 0:
            reach[j] = True
        pre[j+1] = pre[j] + (1 if reach[j] else 0)
    return reach[n-1]

print(can_reach_vii('011010', 2, 3))  # True
print(can_reach_vii('01101110', 2, 3))  # False

เปรียบเทียบวิธีแบบละโมบกับวิธี BFS

เกมกระโดด II มีวิธี O(n) ที่เทียบเท่ากันสองแบบ: การขยายช่วงแบบละโมบ และ การท่องระดับแบบ BFS วิธีแบบละโมบใช้พื้นที่ O(1) (ไม่ต้องใช้คิว) ขณะที่ BFS ใช้พื้นที่ O(n) สำหรับเซตของตำแหน่งที่เยี่ยมชมแล้ว ในการสัมภาษณ์ ควรเลือกวิธีแบบละโมบเพราะใช้พื้นที่อย่างมีประสิทธิภาพมากกว่า อย่างไรก็ตาม BFS หาได้ง่ายกว่าในขั้นแรก — หากคุณยังมองไม่เห็นวิธีแบบละโมบ ให้เขียน BFS เพื่อให้ได้วิธีแก้ที่ทำงานได้ก่อน แล้วจึงปรับให้มีประสิทธิภาพ ทั้งสองวิธีคำนวณจำนวนการกระโดดน้อยที่สุดได้อย่างถูกต้อง

# Both approaches are O(n) time
# Greedy: O(1) space — preferred in interviews
# BFS: O(n) space — easier to derive

# Greedy advantage: no auxiliary data structures
def jump_greedy(nums):
    n, jumps, curr, far = len(nums), 0, 0, 0
    for i in range(n-1):
        far = max(far, i+nums[i])
        if i == curr: jumps += 1; curr = far
    return jumps

# BFS equivalence: each level = one jump
from collections import deque
def jump_bfs(nums):
    n = len(nums)
    if n == 1: return 0
    q, visited, level = deque([0]), {0}, 0
    while q:
        level += 1
        for _ in range(len(q)):
            pos = q.popleft()
            for j in range(1, nums[pos]+1):
                nxt = pos + j
                if nxt >= n-1: return level
                if nxt not in visited: visited.add(nxt); q.append(nxt)
    return -1

nums = [2,3,1,1,4]
print(jump_greedy(nums), '==', jump_bfs(nums))  # both 2

สรุปความซับซ้อนของเกมกระโดด

สรุปความซับซ้อนของเกมกระโดดแต่ละรูปแบบ: เกมกระโดด I (การเข้าถึง): ใช้เวลา O(n) และพื้นที่ O(1) เกมกระโดด II (การกระโดดน้อยที่สุดด้วยวิธีแบบละโมบ): ใช้เวลา O(n) และพื้นที่ O(1) เกมกระโดด II (BFS): ใช้เวลา O(n) และพื้นที่ O(n) เกมกระโดด II (DP): ใช้เวลา O(n × ค่าการกระโดดสูงสุด) และพื้นที่ O(n) เกมกระโดด III (BFS/DFS): ใช้เวลา O(n) และพื้นที่ O(n) สำหรับตำแหน่งที่เยี่ยมชมแล้ว เกมกระโดด VII (หน้าต่างเลื่อน): ใช้เวลา O(n) และพื้นที่ O(n) ในการสัมภาษณ์ ควรนำเสนอวิธีแบบละโมบ O(n) O(1) สำหรับเกมกระโดด I และ II เสมอ

# Comparison: all versions on the same input
nums = [2, 3, 1, 1, 4]

# Jump I
def can_jump(nums):
    mr = 0
    for i, j in enumerate(nums):
        if i > mr: return False
        mr = max(mr, i+j)
    return True

# Jump II greedy O(n) O(1)
def jump_min(nums):
    n, jumps, curr, far = len(nums), 0, 0, 0
    for i in range(n-1):
        far = max(far, i+nums[i])
        if i == curr:
            jumps += 1; curr = far
            if curr >= n-1: break
    return jumps

print('Can reach:', can_jump(nums))    # True
print('Min jumps:', jump_min(nums))    # 2
print('Complexity: O(n) time, O(1) space')

ตรวจสอบอย่างรวดเร็ว

ทดสอบความเข้าใจแนวคิดโครงสร้างข้อมูล & อัลกอริทึม — การเตรียมตัวสัมภาษณ์การเขียนโปรแกรมจากบทเรียนนี้

สรุปบทเรียน

ในบทเรียนนี้ คุณได้เรียนรู้ว่า เกมกระโดด I ใช้การติดตาม max_reach แบบละโมบเพื่อพิจารณาการเข้าถึง โดยใช้เวลา O(n) และพื้นที่ O(1) เกมกระโดด II ใช้การขยายช่วงร่วมกับ curr_end และ farthest เพื่อนับจำนวนการกระโดดน้อยที่สุด โดยใช้เวลา O(n) และพื้นที่ O(1) และ การขยายช่วงแบบละโมบเทียบเท่ากับ BFS ทีละระดับโดยไม่ต้องเสียค่าใช้จ่ายเพิ่มเติมจากคิว ต่อไปเราจะประยุกต์ใช้การให้เหตุผลแบบละโมบกับช่วงพักของตัวจัดตารางงาน และปัญหาความเป็นไปได้ของการเดินทางเป็นวงรอบในปัญหาสถานีบริการน้ำมัน

คำถามที่พบบ่อย

บทเรียน “เกมกระโดด I และ II” ฟรีหรือไม่

ใช่ — ข้อความเต็มของ “เกมกระโดด I และ II” ฟรีให้อ่านที่นี่บนเว็บ เพื่อปฏิบัติแบบโต้ตอบ (ตัวแก้ไขโค้ดในตัวและติวเตอร์ AI ตลอด 24/7) และปลดล็อคส่วนที่เหลือของคอร์ส Coding Interview Prep ให้อัปเกรดเป็น CoddyKit PRO คอร์ส Coding Interview Prep มีบทเรียนทั้งหมด 4 บทเรียน

คุณจะเรียนรู้อะไรในบทเรียน “เกมกระโดด I และ II”

ตรวจสอบว่าสามารถไปถึงจุดหมายได้หรือไม่และหาจำนวนครั้งกระโดดต่ำสุดด้วยวิธีละโมบที่ขยายช่วง โดยไม่ต้องใช้ DP คุณปฏิบัติ Coding Interview Prep ด้วยโค้ดที่ใช้งานได้จริงที่คุณเรียกใช้โดยตรงในเบราว์เซอร์ และติวเตอร์ AI ตลอด 24/7 ตอบคำถามของคุณขณะที่คุณไปผ่านบทเรียน

คุณต้องมีประสบการณ์ก่อนที่จะเริ่มเรียน Coding Interview Prep หรือไม่

ไม่จำเป็นต้องมีประสบการณ์มาก่อน Coding Interview Prep บน CoddyKit ออกแบบมาสำหรับผู้เริ่มต้นไปจนถึงผู้เรียนขั้นสูง คุณสามารถเริ่มต้นที่นี่หรือเริ่มจากตัวแรกและเรียนด้วยความเร็วของคุณเอง นี่คือบทเรียนที่ 3 จากทั้งหมด 4 บทเรียน

บทเรียน “เกมกระโดด I และ II” ใช้เวลานานแค่ไหน

บทเรียน CoddyKit ส่วนใหญ่ใช้เวลาประมาณ 5–10 นาที แต่ละบทเรียนจึงสั้นและเป็นแบบโต้ตอบ คุณสามารถก้าวหน้าอย่างต่อเนื่องและกลับมาเรียนต่อจากตรงที่เพิ่งหยุดบนเว็บและแอปได้เลย

ฉันเขียนและรันโค้ดในบทเรียน Coding Interview Prep นี้ได้ไหม

ได้ บทเรียน Coding Interview Prep ทุกบทมีตัวแก้ไขโค้ดในตัว คุณจึงเขียนและรันโค้ดจริงได้เลยในเบราว์เซอร์ และได้รับข้อเสนอแนะจาก AI ในทันที — ไม่ต้องติดตั้งในเครื่องของคุณ

บทเรียนทั้งหมดในหลักสูตรนี้

  1. ละโมบกับ DP: ควรใช้แบบใด
  2. การจัดตารางช่วงเวลาและการรวมช่วง
  3. เกมกระโดด I และ II
  4. ตัวจัดตารางงานและสถานีเติมน้ำมัน
← กลับไปที่ Coding Interview Prep