CodeGround
DashboardProblemsInterviewsAssessmentsCourses
CodeGround
DashboardProblemsInterviewsAssessmentsCourses
The Technical Interview Course

0 of 170 lessons complete

  • When is a local choice safe?
  • Reachability as one running number
  • Counting steps without a queue
  • One pass with a reset
  • When one element has no options
  • Discard what can never help, then combine
  • Deciding where a group ends
  • Carrying a range instead of a value
  • Retrieval: greedy or a table?

Greedy

148 / 170

Greedy/Lesson 2 of 9·About 39 min

Reachability as one running number

You do not need to know how you got somewhere. Only how far anything can now take you.

Key idea

The obvious approaches, and what they cost

Given positions each allowing a jump of some length, can you reach the end? The search version explores every route and is exponential. The table version asks whether each position is reachable and is quadratic, since each position must check everything that could reach it.

Both work. The greedy version is one pass, and its justification is short enough to give out loud.

Why it works

Carry the furthest reachable position

Walk forward keeping one number: the furthest position anything seen so far can reach. At each position, if that number has not reached where you are standing, nothing can, and the answer is no.

Otherwise extend it with what this position offers. The key observation is that how you reached a position is irrelevant. If it is reachable at all, everything it can reach is reachable.

That is what collapses the table. Reachability is not a property of routes, it is a property of positions, and one running maximum captures all of it.

Watch that single number on an input where the walk does get stranded.

1jumps = [3, 2, 1, 0, 4]2 3furthest = 04for position, jump in enumerate(jumps):5    reached = position <= furthest6    print(f"at {position}: furthest so far {furthest}, standing here is"7          f" {'fine' if reached else 'impossible'}")8    if not reached:9        break10    furthest = max(furthest, position + jump)
The zero at position 3 adds nothing, so the reach stalls and position 4 is never available.

Gotcha

Check before extending, not after

The stranded check runs before the running maximum is updated with this position. Updating first would let an unreachable position contribute its own jump, which is exactly the thing that cannot happen.

Reversing those two lines produces a function that says yes to the input above, since position 4 would extend the reach using a jump it never had the chance to make.

Edge cases

A single position is already at the end

[0] is reachable because you start at the end. A solution that requires a positive jump somewhere gets this wrong, and it is the kind of case that only appears in the hidden tests.

The empty input is a judgement call the problem should specify. Returning True treats it as vacuously satisfied, which is the usual convention.

Tip

The backward formulation

There is an equally common version that walks from the end, tracking the leftmost position known to reach the goal, and asks at the end whether that position is the start.

It is the same algorithm mirrored and neither is better. Pick one and be able to state its invariant; mixing the two is where the confusion comes from.

Cost

Cost

One pass, constant work per position, so O(n) time and O(1) space.

Against the quadratic table and the exponential search, that is worth stating explicitly. The saving comes entirely from the observation that routes do not matter, which is the insight rather than the code.

Predict the output· 5 min

Predict the output: extending before checking

This updates the running reach before checking whether the current position was reachable. The first input is not finishable and the second is. Type the two values it prints.

Python
1def can_finish_broken(jumps):2    furthest = 03    for position, jump in enumerate(jumps):4        furthest = max(furthest, position + jump)5        if position > furthest:6            return False7    return True8 9print(can_finish_broken([3, 2, 1, 0, 4]), can_finish_broken([2, 3, 1, 1, 4]))
Problem· 22 min

Jump Game

Read the constraints first and let them tell you what complexity is expected. Derive the approach, implement it, run the tests, and submit when it passes.

PreviousWhen is a local choice safe?
NextCounting steps without a queue

0 of 3 parts complete