Voorbereiding op programmeerinterviews · Les

Jump Game I en II

Bepaal bereikbaarheid en het minimumaantal sprongen met een greedy-aanpak die bereiken uitbreidt en geen DP nodig heeft.

Les 3 van 413 stappen

Jump Game I en II is een gratis Voorbereiding op programmeerinterviews-les op CoddyKit. Dit is les 3 van 4. Je kunt de volledige les hieronder gratis lezen en daarna in de browser praktisch oefenen met een ingebouwde code-editor en een AI-begeleider die 24/7 beschikbaar is. Deze les maakt deel uit van het leertraject Voorbereiding op programmeerinterviews. Je voortgang wordt gesynchroniseerd op het web en in de CoddyKit-app. De cursus Voorbereiding op programmeerinterviews bevat in totaal 4 lessen.

Sprongspel I: kun je het einde bereiken?

Sprongspel I (LeetCode 55): gegeven een array waarin nums[i] de maximale spronglengte vanaf index i aangeeft, bepaal je of je vanaf index 0 de laatste index kunt bereiken. Bij [2, 3, 1, 1, 4] kun je het einde bereiken (sprong 2→3, waarna 3 je naar het einde brengt). Bij [3, 2, 1, 0, 4] lukt dat niet (je komt altijd op 0 terecht, en die heeft een spronglengte van 0). Een greedy-oplossing draait in 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

Greedy: maximaal bereik bijhouden

Het greedy-inzicht voor Sprongspel I: houd max_reach bij, de verste index die tot nu toe bereikbaar is. Werk bij elke index i max_reach = max(max_reach, i + nums[i]) bij. Als op enig moment i > max_reach geldt, is de huidige index onbereikbaar — geef False terug. Als we de laatste index bereiken of voorbijgaan, geven we True terug. Je hebt geen DP of backtracking nodig.

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

Sprongspel I traceren

Traceer [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 → return False. Het algoritme stelt correct vast dat index 4 onbereikbaar is. Elk pad vanaf index 0 loopt vast, omdat de 0 op index 3 max_reach beperkt tot 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]))

Sprongspel II: minimaal aantal sprongen

Bij Sprongspel II (LeetCode 45) zoek je het minimale aantal sprongen om de laatste index te bereiken (die altijd bereikbaar is). De greedy-aanpak gebruikt een strategie waarbij je het bereik uitbreidt: houd het verste bereik van de huidige sprong (curr_end) en het verste bereik van de volgende sprong (farthest) bij. Wanneer je het bereik van de huidige sprong hebt opgebruikt, moet je springen — verhoog jumps en stel curr_end = farthest in.

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

Sprongspel II visualiseren

Zie Sprongspel II als een BFS-niveau-voor-niveau-aanpak zonder de overhead van de wachtrij. Elke sprong komt overeen met één BFS-niveau. curr_end is de grens van het huidige niveau. farthest is de maximale index die in het volgende niveau bereikbaar is. Wanneer je het huidige niveau volledig hebt gescand (i == curr_end), heb je de grens van het volgende niveau bepaald en moet je het aantal sprongen verhogen. Dit is BFS op een impliciete graaf in O(n)-tijd en met O(1)-geheugenruimte.

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]))

Waarom greedy correct is voor Sprongspel II

Waarom levert greedy, waarbij je altijd tot het verste punt uitbreidt, het minimale aantal sprongen op? Uitwisselingsargument: stel dat de optimale oplossing een sprong maakt die het verste punt niet bereikt. Dan kunnen we die sprong altijd uitbreiden tot farthest zonder extra kosten — het blijft één sprong. Door per sprong altijd het maximale bereik te nemen, garanderen we het minimale aantal benodigde sprongen. Een oplossing die per sprong een kleiner bereik neemt, kan het niet beter doen en heeft meer sprongen nodig om dezelfde afstand af te leggen.

# 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-alternatief voor Sprongspel II

Een DP-oplossing: dp[i] = het minimale aantal sprongen om index i te bereiken. Werk voor elke positie j alle bereikbare posities bij: dp[j+k] = min(dp[j+k], dp[j]+1) voor k in 1..nums[j]. Dit kost O(n × max_jump)-tijd en O(n)-geheugenruimte — veel langzamer dan de greedy-oplossing met O(n). Greedy is hier beter; DP wordt ter vergelijking getoond om te laten zien hoe greedy de binnenste lus kan vermijden.

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

Sprongspel III: index nul bereiken

Sprongspel III (LeetCode 1306): begin op een gegeven index; vanaf index i spring je naar i + nums[i] of i - nums[i]. Kun je een index met waarde 0 bereiken? Dit is een bereikbaarheidsprobleem (BFS/DFS), geen minimalisatieprobleem — greedy is hier niet van toepassing. Gebruik BFS met een verzameling bezochte posities om cycli te voorkomen. Tijdcomplexiteit: 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)

Sprongspel VII: bereikbaar met een bereik

Sprongspel VII (LeetCode 1871): kun je een binaire tekenreeks doorlopen door van index 0 naar de laatste index te springen, waarbij je vanaf positie i naar elke '0' in [i+minJump, i+maxJump] kunt springen? Gebruik een som over een schuivend venster van de bereikbare array. Houd een prefixsom van bereikbare posities bij; een positie j is bereikbaar als er een bereikbare positie in [j-maxJump, j-minJump] staat.

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

Greedy- en BFS-oplossingen vergelijken

Sprongspel II heeft twee gelijkwaardige O(n)-aanpakken: greedy-bereikuitbreiding en BFS-niveaudoorloop. De greedy-aanpak gebruikt O(1)-geheugenruimte (geen wachtrij), terwijl BFS O(n) gebruikt voor de verzameling bezochte posities. Tijdens een sollicitatiegesprek heeft greedy de voorkeur vanwege de geheugenefficiëntie. BFS is echter gemakkelijker eerst af te leiden — als je moeite hebt om de greedy-oplossing te zien, schrijf dan BFS om een werkende oplossing te krijgen en optimaliseer die daarna. Beide berekenen correct het minimale aantal sprongen.

# 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

Complexiteitsoverzicht van Sprongspel

Complexiteitsoverzicht van de varianten van Sprongspel: Sprong I (bereikbaarheid): O(n)-tijd, O(1)-geheugenruimte. Sprong II (greedy voor minimaal aantal sprongen): O(n)-tijd, O(1)-geheugenruimte. Sprong II (BFS): O(n)-tijd, O(n)-geheugenruimte. Sprong II (DP): O(n × max_jump)-tijd, O(n)-geheugenruimte. Sprong III (BFS/DFS): O(n)-tijd, O(n)-geheugenruimte voor bezochte posities. Sprong VII (schuivend venster): O(n)-tijd, O(n)-geheugenruimte. Presenteer tijdens sollicitatiegesprekken altijd de greedy-oplossing met O(n)-tijd en O(1)-geheugenruimte voor Sprong I en 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')

Korte controle

Toets je begrip van de concepten uit Data Structures & Algorithms — Coding Interview Prep in deze les.

Samenvatting van de les

In deze les heb je geleerd: Sprongspel I gebruikt greedy-tracking van max_reach om bereikbaarheid te bepalen in O(n)-tijd en met O(1)-geheugenruimte, Sprongspel II gebruikt bereikuitbreiding met curr_end en farthest om het minimale aantal sprongen te tellen in O(n)-tijd en met O(1)-geheugenruimte, en greedy-bereikuitbreiding is gelijkwaardig aan BFS-niveau voor niveau, maar zonder de overhead van een wachtrij. Hierna passen we greedy-redeneringen toe op de afkoelperiode van het taakplanningsprobleem en op de problemen rond de haalbaarheid van een rondrit bij tankstations.

Gratis beginnen

Leer Voorbereiding op programmeerinterviews met een AI-tutor — gratis

Schrijf echte code en voer die uit in je browser, krijg direct hulp van een AI-tutor die 24/7 beschikbaar is en ga verder waar je gebleven bent op het web of in de app.

Cursussen
90
Lessen
360

Veelgestelde vragen

Is de les “Jump Game I en II” gratis?

Ja — de volledige tekst van “Jump Game I en II” kun je hier gratis op het web lezen. Als je interactief wilt oefenen met een ingebouwde code-editor en een AI-begeleider die 24/7 beschikbaar is, en de rest van de cursus Voorbereiding op programmeerinterviews wilt ontgrendelen, kun je upgraden naar CoddyKit PRO. De cursus Voorbereiding op programmeerinterviews bevat in totaal 4 lessen.

Wat leer ik in “Jump Game I en II”?

Bepaal bereikbaarheid en het minimumaantal sprongen met een greedy-aanpak die bereiken uitbreidt en geen DP nodig heeft. Je oefent met Voorbereiding op programmeerinterviews door code rechtstreeks in de browser uit te voeren. Een AI-begeleider die 24/7 beschikbaar is beantwoordt je vragen terwijl je de les doorwerkt.

Heb ik ervaring nodig om met Voorbereiding op programmeerinterviews te beginnen?

Ervaring vooraf is niet nodig. Voorbereiding op programmeerinterviews op CoddyKit is opgebouwd voor beginners tot gevorderden, zodat je hier of bij het begin kunt starten en in je eigen tempo kunt leren. Dit is les 3 van 4.

Hoe lang duurt de les “Jump Game I en II”?

De meeste lessen van CoddyKit duren ongeveer 5–10 minuten. Elke les is kort en interactief, zodat je gestaag vooruitgaat en op het web en in de app precies verdergaat waar je was gebleven.

Kan ik code schrijven en uitvoeren in deze les over Voorbereiding op programmeerinterviews?

Ja. Elke les over Voorbereiding op programmeerinterviews bevat een ingebouwde code-editor, zodat je rechtstreeks in je browser echte code kunt schrijven en uitvoeren en direct feedback van AI krijgt — lokale installatie is niet nodig.

Alle lessen in deze cursus

  1. Greedy versus DP: wanneer gebruikt u welke methode
  2. Intervalplanning en samenvoegen
  3. Jump Game I en II
  4. Task Scheduler en Gas Station
← Terug naar Voorbereiding op programmeerinterviews