Jump Game I und II
Bestimmen Sie Erreichbarkeit und minimale Sprunganzahl mithilfe eines Greedy-Ansatzes zur Bereichserweiterung, der keine dynamische Programmierung benötigt.
Jump Game I und II ist eine kostenlose Coding Interview Prep-Lektion auf CoddyKit. Dies ist Lektion 3 von 4. Du kannst die komplette Lektion unten kostenlos lesen – dann übst du sie direkt im Browser mit einem integrierten Code-Editor und einem KI-Tutor rund um die Uhr. Sie ist Teil des Coding Interview Prep-Lernpfads, und dein Fortschritt wird über Web und CoddyKit-App synchronisiert. Der Coding Interview Prep-Kurs umfasst insgesamt 4 Lektionen.
Jump Game I: Können Sie das Ende erreichen?
Jump Game I (LeetCode 55): Gegeben ist ein Array, in dem nums[i] die maximale Sprungweite vom Index i angibt. Bestimmen Sie, ob Sie ausgehend von Index 0 den letzten Index erreichen können. Für [2, 3, 1, 1, 4] können Sie das Ende erreichen (Sprung von 2 nach 3, und von 3 aus gelangen Sie zum Ende). Für [3, 2, 1, 0, 4] ist dies nicht möglich (Sie landen zwangsläufig auf 0, von wo aus die Sprungweite 0 beträgt). Eine Greedy-Lösung läuft 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 = stuckGreedy: Maximale Reichweite verfolgen
Die entscheidende Greedy-Idee bei Jump Game I: Verwalten Sie max_reach, den bisher am weitesten erreichbaren Index. Aktualisieren Sie bei jedem Index i den Wert mit max_reach = max(max_reach, i + nums[i]). Falls irgendwann i > max_reach gilt, ist der aktuelle Index nicht erreichbar — geben Sie False zurück. Wenn Sie den letzten Index erreichen oder überschreiten, geben Sie True zurück. Dynamische Programmierung oder Backtracking sind nicht erforderlich.
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])) # FalseJump Game I nachvollziehen
Verfolgen wir [3, 2, 1, 0, 4]: i=0, Sprung=3, max_reach=3. i=1, Sprung=2, max_reach=max(3,3)=3. i=2, Sprung=1, max_reach=max(3,3)=3. i=3, Sprung=0, max_reach=max(3,3)=3. i=4, i=4 > max_reach=3 → geben Sie False zurück. Der Algorithmus erkennt korrekt, dass Index 4 nicht erreichbar ist. Jeder Pfad von Index 0 aus ist gefangen, weil die 0 an Index 3 die max_reach auf 3 begrenzt.
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]))Jump Game II: Minimale Anzahl von Sprüngen
Bei Jump Game II (LeetCode 45) soll die minimale Anzahl von Sprüngen ermittelt werden, um den letzten Index zu erreichen (dieser ist immer erreichbar). Der Greedy-Ansatz verwendet eine Strategie zur Erweiterung des erreichbaren Bereichs: Verwalten Sie die größte Reichweite des aktuellen Sprungs (curr_end) und die größte Reichweite des nächsten Sprungs (farthest). Wenn der Bereich des aktuellen Sprungs ausgeschöpft ist, müssen Sie springen — erhöhen Sie jumps und setzen Sie 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])) # 3Jump Game II visualisieren
Betrachten Sie Jump Game II als einen BFS-Durchlauf Ebene für Ebene, jedoch ohne den zusätzlichen Aufwand einer Warteschlange. Jeder Sprung entspricht einer BFS-Ebene. curr_end ist die Grenze der aktuellen Ebene. farthest ist der maximal erreichbare Index der nächsten Ebene. Wenn Sie die aktuelle Ebene vollständig durchlaufen haben (i == curr_end), steht die Grenze der nächsten Ebene fest und Sie müssen den Sprungzähler erhöhen. Dies ist ein BFS auf einem impliziten Graphen mit einer Laufzeit von O(n) und einem Speicherbedarf von 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]))Warum Greedy bei Jump Game II korrekt ist
Warum liefert Greedy (immer die größtmögliche Reichweite zu nutzen) die minimale Anzahl von Sprüngen? Austauschargument: Angenommen, die optimale Lösung führt einen Sprung aus, der nicht den am weitesten entfernten Punkt erreicht. Wir können diesen Sprung immer bis zu farthest erweitern, ohne zusätzliche Kosten zu verursachen — es bleibt ein einziger Sprung. Indem wir bei jedem Sprung stets die maximale Reichweite nutzen, stellen wir die minimale erforderliche Sprunganzahl sicher. Eine Lösung, die pro Sprung eine geringere Reichweite nutzt, kann nicht besser sein und würde mehr Sprünge benötigen, um dieselbe Strecke zurückzulegen.
# 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-Alternative für Jump Game II
Eine DP-Lösung: dp[i] = minimale Anzahl von Sprüngen, um Index i zu erreichen. Aktualisieren Sie für jede Position j alle erreichbaren Positionen: dp[j+k] = min(dp[j+k], dp[j]+1) für k in 1..nums[j]. Dies benötigt O(n × max_jump) Zeit und O(n) Speicher — deutlich mehr als der Greedy-Ansatz mit O(n). Der Greedy-Ansatz ist hier überlegen; die DP-Lösung wird zum Vergleich gezeigt, um zu veranschaulichen, wie Greedy die innere Schleife vermeiden kann.
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 fasterJump Game III: Index 0 erreichen
Jump Game III (LeetCode 1306): Starten Sie an einem vorgegebenen Index. Von Index i aus springen Sie zu i + nums[i] oder i - nums[i]. Können Sie einen beliebigen Index mit dem Wert 0 erreichen? Dies ist ein Erreichbarkeitsproblem (BFS/DFS), kein Minimierungsproblem — Greedy ist hier nicht anwendbar. Verwenden Sie BFS mit einer Menge besuchter Indizes, um Zyklen zu vermeiden. Laufzeit: 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)Jump Game VII: Erreichbarkeit mit einem Bereich
Jump Game VII (LeetCode 1871): Können Sie eine Binärzeichenkette durchlaufen, indem Sie von Index 0 zum letzten Index springen, wobei Sie von Position i zu jeder '0' in [i+minJump, i+maxJump] springen können? Verwenden Sie eine Sliding-Window-Summe über dem Array der erreichbaren Positionen. Verwalten Sie eine Präfixsumme der erreichbaren Positionen. Eine Position j ist erreichbar, wenn es eine erreichbare Position in [j-maxJump, j-minJump] gibt.
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)) # FalseGreedy- und BFS-Lösungen vergleichen
Für Jump Game II gibt es zwei gleichwertige Ansätze mit O(n): die Greedy-Erweiterung des erreichbaren Bereichs und der BFS-Durchlauf nach Ebenen. Der Greedy-Ansatz benötigt O(1) Speicher (keine Warteschlange), während BFS O(n) für die Menge der besuchten Indizes benötigt. In einem Vorstellungsgespräch wird Greedy wegen des geringeren Speicherbedarfs bevorzugt. BFS lässt sich jedoch zunächst leichter herleiten — wenn Sie die Greedy-Lösung nicht erkennen, implementieren Sie zunächst BFS, um eine funktionierende Lösung zu erhalten, und optimieren Sie sie anschließend. Beide Ansätze berechnen die minimale Anzahl von Sprüngen korrekt.
# 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 2Zusammenfassung der Komplexität von Jump Game
Zusammenfassung der Komplexität der verschiedenen Jump-Game-Varianten: Jump I (Erreichbarkeit): O(n) Zeit, O(1) Speicher. Jump II (Greedy für minimale Sprunganzahl): O(n) Zeit, O(1) Speicher. Jump II (BFS): O(n) Zeit, O(n) Speicher. Jump II (DP): O(n × max_jump) Zeit, O(n) Speicher. Jump III (BFS/DFS): O(n) Zeit, O(n) Speicher für die besuchten Indizes. Jump VII (Sliding Window): O(n) Zeit, O(n) Speicher. Präsentieren Sie in Vorstellungsgesprächen für Jump I und II stets die Greedy-Lösung mit O(n) Zeit und O(1) Speicher.
# 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')Kurztest
Testen Sie Ihr Verständnis der Konzepte aus Data Structures & Algorithms — Coding Interview Prep in dieser Lektion.
Zusammenfassung der Lektion
In dieser Lektion haben Sie gelernt: Jump Game I verwendet die Greedy-Verfolgung von max_reach, um die Erreichbarkeit mit O(n) Zeit und O(1) Speicher zu bestimmen, Jump Game II verwendet mit curr_end und farthest eine Erweiterung des erreichbaren Bereichs, um die minimale Sprunganzahl mit O(n) Zeit und O(1) Speicher zu zählen, und die Greedy-Erweiterung des erreichbaren Bereichs entspricht einem BFS-Durchlauf Ebene für Ebene ohne den zusätzlichen Aufwand einer Warteschlange. Als Nächstes wenden wir Greedy-Überlegungen auf die Abkühlphase beim Task Scheduler und die Probleme zur Erreichbarkeit im Kreis beim Gas Station an.
Häufig gestellte Fragen
Ist die Lektion „Jump Game I und II“ kostenlos?
Ja — der vollständige Text von „Jump Game I und II“ ist hier im Web kostenlos zu lesen. Um sie interaktiv zu üben (integrierter Code-Editor und 24/7 KI-Tutor) und den Rest des Coding Interview Prep-Kurses freizuschalten, upgrade auf CoddyKit PRO. Der Coding Interview Prep-Kurs umfasst insgesamt 4 Lektionen.
Was lerne ich in „Jump Game I und II“?
Bestimmen Sie Erreichbarkeit und minimale Sprunganzahl mithilfe eines Greedy-Ansatzes zur Bereichserweiterung, der keine dynamische Programmierung benötigt. Du übst Coding Interview Prep mit praktischem Code, den du direkt im Browser ausführst, und ein 24/7 KI-Tutor beantwortet deine Fragen während du die Lektion bearbeitest.
Brauche ich Erfahrung, um Coding Interview Prep zu starten?
Keine Vorkenntnisse erforderlich. Coding Interview Prep auf CoddyKit ist für Anfänger bis fortgeschrittene Lernende strukturiert, sodass du hier starten oder von Anfang an beginnen und in deinem eigenen Tempo voranschreiten kannst. Dies ist Lektion 3 von 4.
Wie lange dauert die Lektion „Jump Game I und II“?
Die meisten CoddyKit-Lektionen dauern etwa 5–10 Minuten. Jede ist kompakt und interaktiv, sodass du stetig Fortschritte machst und genau dort weitermachst, wo du aufgehört hast – im Web und in der App.
Kann ich in dieser Coding Interview Prep-Lektion Code schreiben und ausführen?
Ja. Jede Coding Interview Prep-Lektion enthält einen integrierten Code-Editor, sodass du echten Code direkt in deinem Browser schreibst und ausführst und sofort KI-Feedback erhältst — ohne lokale Einrichtung erforderlich.
Alle Lektionen in diesem Kurs
- Greedy vs. dynamische Programmierung: Wann wird was verwendet?
- Intervallplanung und Zusammenführen
- Jump Game I und II
- Task Scheduler und Gas Station