Jogo dos saltos I e II
Determine a alcançabilidade e o número mínimo de saltos usando uma abordagem gulosa de expansão de intervalos que dispensa DP.
Jogo dos saltos I e II é uma aula grátis de Coding Interview Prep no CoddyKit. Esta é a aula 3 de 4. Você pode ler a aula completa abaixo gratuitamente — depois pratica ao vivo no navegador com um editor de código integrado e um tutor de IA 24/7. Faz parte do caminho de aprendizado de Coding Interview Prep, e seu progresso é sincronizado entre a web e o app CoddyKit. O curso de Coding Interview Prep inclui 4 aulas no total.
Jogo de Saltos I: Você consegue chegar ao fim?
Jogo de Saltos I (LeetCode 55): dado um vetor no qual nums[i] é o comprimento máximo do salto a partir do índice i, determine se é possível chegar ao último índice começando no índice 0. Para [2, 3, 1, 1, 4], é possível chegar ao fim (salto 2→3 e, em seguida, 3 leva você ao fim). Para [3, 2, 1, 0, 4], não é possível (você sempre cai em 0, que tem salto 0). Uma solução gulosa é executada em 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 = stuckAbordagem Gulosa: Acompanhe o Alcance Máximo
A ideia gulosa para o Jogo de Saltos I é manter max_reach, o índice mais distante que pode ser alcançado até o momento. Em cada índice i, atualize max_reach = max(max_reach, i + nums[i]). Se, em algum momento, i > max_reach, o índice atual não pode ser alcançado — retorne False. Se chegarmos ao último índice ou passarmos dele, retorne True. Não é necessário usar DP nem retrocesso.
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])) # FalseRastreando o Jogo de Saltos I
Rastreie [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 → retorne False. O algoritmo identifica corretamente que o índice 4 não pode ser alcançado. Todo caminho a partir do índice 0 fica preso, pois o 0 no í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]))Jogo de Saltos II: Número Mínimo de Saltos
Jogo de Saltos II (LeetCode 45) pede o número mínimo de saltos para chegar ao último índice (que é sempre alcançável). A abordagem gulosa usa uma estratégia de expansão do alcance: mantenha o alcance mais distante do salto atual (curr_end) e o alcance mais distante do salto seguinte (farthest). Quando esgotar o alcance do salto atual, será necessário saltar — incremente jumps e defina 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])) # 3Visualizando o Jogo de Saltos II
Pense no Jogo de Saltos II como uma abordagem BFS nível a nível, sem o custo adicional da fila. Cada salto corresponde a um nível de BFS. curr_end é o limite do nível atual. farthest é o índice máximo alcançável no nível seguinte. Quando terminar de percorrer o nível atual (i == curr_end), você terá determinado o limite do nível seguinte e deverá incrementar a contagem de saltos. Esta é uma BFS em um grafo implícito, com tempo O(n) e espaço 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 que a Abordagem Gulosa está Correta para o Jogo de Saltos II
Por que a abordagem gulosa (sempre estender até o ponto mais distante) produz o número mínimo de saltos? Argumento de troca: suponha que a solução ótima faça um salto que não alcance o ponto mais distante. Sempre podemos estender esse salto até farthest sem custo adicional — ele continua sendo um único salto. Ao escolher sempre o alcance máximo por salto, garantimos o número mínimo de saltos necessário. Qualquer solução que alcance uma distância menor por salto não poderá obter um resultado melhor e precisará de mais saltos para cobrir a mesma distância.
# 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 com DP para o Jogo de Saltos II
Uma solução com DP: dp[i] = número mínimo de saltos para chegar ao índice i. Para cada posição j, atualize todas as posições alcançáveis: dp[j+k] = min(dp[j+k], dp[j]+1) para k no intervalo 1..nums[j]. Isso é executado em tempo O(n × max_jump) e espaço O(n) — muito mais lentamente do que a solução gulosa O(n). A abordagem gulosa é superior neste caso; o DP é apresentado para comparação, mostrando como a abordagem gulosa pode evitar o laço 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 fasterJogo de Saltos III: Alcance o Índice Zero
Jogo de Saltos III (LeetCode 1306): comece em um índice fornecido; a partir do índice i, salte para i + nums[i] ou i - nums[i]. É possível chegar a algum índice cujo valor seja 0? Este é um problema de alcançabilidade (BFS/DFS), não um problema de minimização — a abordagem gulosa não se aplica. Use BFS com um conjunto de visitados para evitar ciclos. Tempo: 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)Jogo de Saltos VII: Alcançável com um Intervalo
Jogo de Saltos VII (LeetCode 1871): é possível percorrer uma cadeia binária saltando do índice 0 para o último índice, sabendo que, a partir da posição i, você pode saltar para qualquer '0' em [i+minJump, i+maxJump]? Use uma soma em janela deslizante sobre o vetor de posições alcançáveis. Mantenha uma soma de prefixos das posições alcançáveis; uma posição j pode ser alcançada se houver uma posição alcançável em [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)) # FalseComparando Soluções Gulosas e com BFS
O Jogo de Saltos II tem duas abordagens equivalentes em O(n): expansão gulosa do alcance e travessia de níveis com BFS. A abordagem gulosa usa espaço O(1) (sem fila), enquanto BFS usa O(n) para o conjunto de visitados. Em uma entrevista, a abordagem gulosa é preferida por sua eficiência de espaço. No entanto, BFS é mais fácil de derivar inicialmente — se você tiver dificuldade para identificar a solução gulosa, escreva o código de BFS para obter uma solução funcional e depois a otimize. Ambas calculam corretamente o 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 2Resumo da Complexidade dos Jogos de Saltos
Resumo da complexidade das variantes do Jogo de Saltos: Jogo I (alcançabilidade): tempo O(n), espaço O(1). Jogo II (número mínimo de saltos com abordagem gulosa): tempo O(n), espaço O(1). Jogo II (BFS): tempo O(n), espaço O(n). Jogo II (DP): tempo O(n × max_jump), espaço O(n). Jogo III (BFS/DFS): tempo O(n), espaço O(n) para os visitados. Jogo VII (janela deslizante): tempo O(n), espaço O(n). Em entrevistas, apresente sempre a solução gulosa O(n) O(1) para os Jogos I e 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')Verificação Rápida
Teste sua compreensão dos conceitos de Estruturas de Dados e Algoritmos — Preparação para Entrevistas de Programação desta lição.
Recapitulação da Lição
Nesta lição, você aprendeu: o Jogo de Saltos I usa o acompanhamento guloso de max_reach para determinar a alcançabilidade em tempo O(n) e espaço O(1), o Jogo de Saltos II usa a expansão do alcance com curr_end e farthest para contar o número mínimo de saltos em tempo O(n) e espaço O(1) e a expansão gulosa do alcance equivale à BFS nível a nível sem o custo adicional da fila. A seguir, aplicaremos o raciocínio guloso ao período de espera do Agendador de Tarefas e aos problemas de possibilidade de completar o circuito do Posto de Combustível.
Perguntas Frequentes
A aula “Jogo dos saltos I e II” é grátis?
Sim — o texto completo de “Jogo dos saltos I e II” é grátis para ler aqui na web. Para praticá-la interativamente (um editor de código integrado e um tutor de IA 24/7) e desbloquear o restante do curso de Coding Interview Prep, atualize para CoddyKit PRO. O curso de Coding Interview Prep inclui 4 aulas no total.
O que vou aprender em “Jogo dos saltos I e II”?
Determine a alcançabilidade e o número mínimo de saltos usando uma abordagem gulosa de expansão de intervalos que dispensa DP. Você pratica Coding Interview Prep com código prático que executa diretamente no navegador, e um tutor de IA 24/7 responde suas dúvidas enquanto trabalha na aula.
Preciso ter experiência prévia para começar Coding Interview Prep?
Nenhuma experiência prévia é necessária. Coding Interview Prep no CoddyKit é estruturado para alunos iniciantes até avançados, então você pode começar aqui ou desde o início e aprender no seu ritmo. Esta é a aula 3 de 4.
Quanto tempo leva a aula “Jogo dos saltos I e II”?
A maioria das aulas CoddyKit leva cerca de 5–10 minutos. Cada uma é compacta e interativa, então você faz progresso constante e retoma exatamente de onde parou entre web e app.
Posso escrever e executar código nesta aula de Coding Interview Prep?
Sim. Cada aula de Coding Interview Prep inclui um editor de código integrado, então você escreve e executa código real direto no navegador e recebe feedback de IA instantaneamente — nenhuma configuração local necessária.
Todas as aulas deste curso
- Algoritmos gulosos vs DP: quando usar cada um
- Escalonamento e fusão de intervalos
- Jogo dos saltos I e II
- Escalonador de tarefas e posto de combustível