0Pricing
Coding Interview Prep · Aula

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 = stuck

Abordagem 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]))        # False

Rastreando 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])) # 3

Visualizando 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 faster

Jogo 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))  # False

Comparando 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 2

Resumo 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

  1. Algoritmos gulosos vs DP: quando usar cada um
  2. Escalonamento e fusão de intervalos
  3. Jogo dos saltos I e II
  4. Escalonador de tarefas e posto de combustível
← Voltar para Coding Interview Prep