Jump Game I y II
Determine la alcanzabilidad y el número mínimo de saltos mediante un enfoque voraz de expansión de rangos que evita la necesidad de programación dinámica.
Jump Game I y II es una lección gratuita de Coding Interview Prep en CoddyKit. Esta es la lección 3 de 4. Puedes leer la lección completa abajo gratuitamente — luego la practicas en el navegador con un editor de código integrado y un tutor de IA 24/7. Forma parte de la ruta de aprendizaje de Coding Interview Prep, y tu progreso se sincroniza en la web y la app de CoddyKit. El curso de Coding Interview Prep incluye 4 lecciones en total.
Juego de saltos I: ¿Puede alcanzar el final?
Juego de saltos I (LeetCode 55): dado un array en el que nums[i] es la longitud máxima del salto desde el índice i, determine si puede alcanzar el último índice empezando desde el índice 0. Para [2, 3, 1, 1, 4], puede llegar al final (salto 2→3 y después 3 le permite llegar al final). Para [3, 2, 1, 0, 4], no puede (siempre cae en 0, que tiene un salto de 0). Una solución greedy se ejecuta en 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: seguimiento del alcance máximo
La idea greedy para Juego de saltos I es mantener max_reach, el índice más lejano que se puede alcanzar hasta el momento. En cada índice i, actualice max_reach = max(max_reach, i + nums[i]). Si en algún momento i > max_reach, el índice actual es inalcanzable: devuelva False. Si alcanzamos o superamos el último índice, devuelva True. No se necesita DP ni backtracking.
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])) # FalseSeguimiento de Juego de saltos I
Trace [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 → devuelva False. El algoritmo identifica correctamente que el índice 4 es inalcanzable. Todas las rutas desde el índice 0 quedan atrapadas porque el 0 del índice 3 limita max_reach a 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]))Juego de saltos II: mínimo de saltos
Juego de saltos II (LeetCode 45) solicita el número mínimo de saltos para alcanzar el último índice (siempre es alcanzable). El enfoque greedy utiliza una estrategia de expansión del rango: mantenga el alcance más lejano del salto actual (curr_end) y el alcance más lejano del salto siguiente (farthest). Cuando agote el rango del salto actual, debe realizar un salto: incremente jumps y establezca 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])) # 3Visualización de Juego de saltos II
Considere Juego de saltos II como un enfoque BFS nivel por nivel, sin la sobrecarga de la cola. Cada salto corresponde a un nivel de BFS. curr_end es el límite del nivel actual. farthest es el índice máximo alcanzable en el nivel siguiente. Cuando termine de recorrer el nivel actual (i == curr_end), habrá determinado el límite del nivel siguiente y deberá incrementar el contador de saltos. Es un BFS sobre un grafo implícito con complejidad temporal O(n) y espacial 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]))Por qué greedy es correcto para Juego de saltos II
¿Por qué greedy (extenderse siempre hasta el punto más lejano) proporciona el número mínimo de saltos? Argumento de intercambio: suponga que la solución óptima realiza un salto que no alcanza el punto más lejano. Siempre podemos extender ese salto hasta alcanzar farthest sin coste adicional: sigue siendo un solo salto. Al tomar siempre el rango máximo en cada salto, garantizamos el número mínimo de saltos necesario. Ninguna solución que recorra un rango menor en cada salto puede obtener un resultado mejor; necesitaría más saltos para cubrir la misma distancia.
# 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))Alternativa con DP para Juego de saltos II
Una solución con DP: dp[i] = número mínimo de saltos para alcanzar el índice i. Para cada posición j, actualice todas las posiciones alcanzables: dp[j+k] = min(dp[j+k], dp[j]+1) para k en 1..nums[j]. Esto se ejecuta en O(n × max_jump) de tiempo y O(n) de espacio, mucho más lento que la solución greedy O(n). Aquí greedy es superior; se muestra DP como comparación para ilustrar cómo greedy puede evitar el bucle interno.
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 fasterJuego de saltos III: alcanzar el índice cero
Juego de saltos III (LeetCode 1306): comience en un índice determinado; desde el índice i, salte a i + nums[i] o a i - nums[i]. ¿Puede alcanzar algún índice cuyo valor sea 0? Este es un problema de alcanzabilidad (BFS/DFS), no de minimización; greedy no se aplica. Utilice BFS con un conjunto de visitados para evitar ciclos. Complejidad temporal: 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)Juego de saltos VII: alcanzabilidad mediante rangos
Juego de saltos VII (LeetCode 1871): ¿puede recorrer una cadena binaria saltando desde el índice 0 hasta el último índice, si desde la posición i puede saltar a cualquier '0' en [i+minJump, i+maxJump]? Utilice una suma de ventana deslizante sobre el array de alcanzabilidad. Mantenga una suma de prefijos de las posiciones alcanzables; una posición j es alcanzable si existe una posición alcanzable en [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)) # FalseComparación de las soluciones greedy y BFS
Juego de saltos II tiene dos enfoques equivalentes de O(n): la expansión greedy del rango y el recorrido BFS por niveles. El enfoque greedy utiliza O(1) de espacio, sin cola, mientras que BFS utiliza O(n) para el conjunto de visitados. En una entrevista, se prefiere greedy por su eficiencia espacial. Sin embargo, BFS es más fácil de deducir primero; si le cuesta ver la solución greedy, escriba BFS para obtener una solución funcional y después optimícela. Ambos calculan correctamente el número mínimo de saltos.
# 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 2Resumen de complejidad de Juego de saltos
Resumen de complejidad de las variantes de Juego de saltos: Juego I (alcanzabilidad): O(n) de tiempo y O(1) de espacio. Juego II (greedy para el número mínimo de saltos): O(n) de tiempo y O(1) de espacio. Juego II (BFS): O(n) de tiempo y O(n) de espacio. Juego II (DP): O(n × max_jump) de tiempo y O(n) de espacio. Juego III (BFS/DFS): O(n) de tiempo y O(n) de espacio para los visitados. Juego VII (ventana deslizante): O(n) de tiempo y O(n) de espacio. En las entrevistas, presente siempre la solución greedy O(n) y O(1) para Juego I y 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')Comprobación rápida
Compruebe su comprensión de los conceptos de Data Structures & Algorithms — Coding Interview Prep de esta lección.
Resumen de la lección
En esta lección ha aprendido: Juego de saltos I utiliza el seguimiento greedy de max_reach para determinar la alcanzabilidad en O(n) de tiempo y O(1) de espacio, Juego de saltos II utiliza la expansión del rango con curr_end y farthest para contar el número mínimo de saltos en O(n) y O(1), y la expansión greedy del rango equivale a un BFS nivel por nivel sin la sobrecarga de la cola. A continuación aplicaremos el razonamiento greedy al periodo de enfriamiento del planificador de tareas y a los problemas de factibilidad circular de Gas Station.
Preguntas frecuentes
¿La lección «Jump Game I y II» es gratis?
Sí — el texto completo de «Jump Game I y II» es gratis para leer aquí en la web. Para practicarla de forma interactiva (editor de código integrado y tutor de IA 24/7) y desbloquear el resto del curso de Coding Interview Prep, actualiza a CoddyKit PRO. El curso de Coding Interview Prep incluye 4 lecciones en total.
¿Qué aprenderé en «Jump Game I y II»?
Determine la alcanzabilidad y el número mínimo de saltos mediante un enfoque voraz de expansión de rangos que evita la necesidad de programación dinámica. Practicas Coding Interview Prep con código real que ejecutas directamente en el navegador, y un tutor de IA 24/7 responde tus preguntas mientras trabajas en la lección.
¿Necesito experiencia previa para empezar Coding Interview Prep?
No se requiere experiencia previa. Coding Interview Prep en CoddyKit está estructurado para principiantes hasta estudiantes avanzados, así que puedes empezar aquí o desde el inicio y avanzar a tu ritmo. Esta es la lección 3 de 4.
¿Cuánto tiempo toma la lección «Jump Game I y II»?
La mayoría de las lecciones de CoddyKit toman alrededor de 5–10 minutos. Cada una es compacta e interactiva, así que avanzas constantemente y retomas exactamente por donde dejaste en la web y la app.
¿Puedo escribir y ejecutar código en esta lección de Coding Interview Prep?
Sí. Cada lección de Coding Interview Prep incluye un editor de código integrado, así que escribes y ejecutas código real directamente en tu navegador y obtienes retroalimentación instantánea de IA — sin configuración local necesaria.
Todas las lecciones de este curso
- Voraces frente a PD: cuándo usar cada enfoque
- Planificación y fusión de intervalos
- Jump Game I y II
- Task Scheduler y Gas Station