Skip to content

Commit 57c3839

Browse files
authored
Merge pull request #120 from BrianLusina/feat/algorithms-greed-jump-game
refactor(algorithms, greedy): jump game algorithm alternatives
2 parents e7de547 + f8ad09c commit 57c3839

37 files changed

+387
-156
lines changed

DIRECTORY.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -85,6 +85,8 @@
8585
* Greedy
8686
* Gas Stations
8787
* [Test Gas Stations](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/greedy/gas_stations/test_gas_stations.py)
88+
* Jump Game
89+
* [Test Jump Game](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/greedy/jump_game/test_jump_game.py)
8890
* Majority Element
8991
* [Test Majority Element](https://github.com/BrianLusina/PythonSnips/blob/master/algorithms/greedy/majority_element/test_majority_element.py)
9092
* Min Arrows
@@ -497,8 +499,6 @@
497499
* [Test H Index](https://github.com/BrianLusina/PythonSnips/blob/master/puzzles/arrays/h_index/test_h_index.py)
498500
* Increasing Triplet Subsequence
499501
* [Test Increasing Triplet](https://github.com/BrianLusina/PythonSnips/blob/master/puzzles/arrays/increasing_triplet_subsequence/test_increasing_triplet.py)
500-
* Jump Game
501-
* [Test Jump Game](https://github.com/BrianLusina/PythonSnips/blob/master/puzzles/arrays/jump_game/test_jump_game.py)
502502
* Kid With Greatest No Of Candies
503503
* [Test Kids With Greatest Candies](https://github.com/BrianLusina/PythonSnips/blob/master/puzzles/arrays/kid_with_greatest_no_of_candies/test_kids_with_greatest_candies.py)
504504
* Longest Increasing Subsequence
Lines changed: 205 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,205 @@
1+
# Jump Game
2+
3+
You are given an integer array nums. You are initially positioned at the array's first index, and each element in the
4+
array represents your maximum jump length at that position.
5+
6+
Return true if you can reach the last index, or false otherwise.
7+
8+
```text
9+
Example 1:
10+
11+
Input: nums = [2,3,1,1,4]
12+
Output: true
13+
Explanation: Jump 1 step from index 0 to 1, then 3 steps to the last index.
14+
```
15+
16+
```text
17+
Example 2:
18+
19+
Input: nums = [3,2,1,0,4]
20+
Output: false
21+
Explanation: You will always arrive at index 3 no matter what. Its maximum jump length is 0, which makes it impossible
22+
to reach the last index.
23+
```
24+
25+
## Solutions
26+
27+
1. [Naive Approach](#naive-approach)
28+
1. [Optimized Approach using Greedy Pattern](#optimized-approach-using-greedy-pattern)
29+
30+
### Naive Approach
31+
32+
The naive approach explores all possible jump sequences from the starting position to the end of the array. It begins
33+
at the first index and attempts to jump to every reachable position from the current index, recursively repeating this
34+
process for each new position. If a path successfully reaches the last index, the algorithm returns success. If it
35+
reaches a position without further moves, it backtracks to try a different path.
36+
37+
While this method guarantees that all possible paths are considered, it is highly inefficient, as it explores many
38+
redundant or dead-end routes. The time complexity of this backtracking approach is exponential, making it impractical
39+
for large inputs.
40+
41+
### Optimized Approach using Greedy Pattern
42+
43+
An optimized way to solve this problem is using a greedy approach that works in reverse. Instead of trying every
44+
possible forward jump, we flip the logic: start from the end and ask, “Can we reach this position from any earlier index?”
45+
46+
We begin with the last index as our initial target—the position we want to reach. Then, we move backward through the
47+
array, checking whether the current index has a jump value large enough to reach or go beyond the current target. If it
48+
can, we update the target to that index. This means that reaching this earlier position would eventually allow us to
49+
reach the end. By continuously updating the target, we’re effectively identifying the leftmost position from which the
50+
end is reachable.
51+
52+
This process continues until we reach the first index or determine that no earlier index can reach the current target.
53+
If we finish with the target at index 0, it means the start of the array can lead to the end, so we return TRUE. If the
54+
target remains beyond index 0, then no path exists from the start to the end, and we return FALSE.
55+
56+
![Solution 1](./images/solutions/jump_game_1_solution_1.png)
57+
![Solution 2](./images/solutions/jump_game_1_solution_2.png)
58+
![Solution 3](./images/solutions/jump_game_1_solution_3.png)
59+
![Solution 4](./images/solutions/jump_game_1_solution_4.png)
60+
![Solution 5](./images/solutions/jump_game_1_solution_5.png)
61+
![Solution 6](./images/solutions/jump_game_1_solution_6.png)
62+
![Solution 7](./images/solutions/jump_game_1_solution_7.png)
63+
![Solution 8](./images/solutions/jump_game_1_solution_8.png)
64+
![Solution 9](./images/solutions/jump_game_1_solution_9.png)
65+
![Solution 10](./images/solutions/jump_game_1_solution_10.png)
66+
![Solution 11](./images/solutions/jump_game_1_solution_11.png)
67+
68+
#### Algorithm
69+
70+
1. We begin by setting the last index of the array as our initial target using the variable target = len(nums) - 1. This
71+
target represents the position we are trying to reach, starting from the end and working backward. By initializing the
72+
target this way, we define our goal: to find out if there is any index i from which the target is reachable based on the
73+
value at that position, nums[i]. This also sets the stage for updating the target if such an index is found.
74+
75+
2. Next, we loop backward through the array using for i in range(len(nums) - 2, -1, -1). Here, i represents the current
76+
index we are analyzing. At each index i, the value nums[i] tells us how far we can jump forward from that position.
77+
By checking whether i + nums[i] >= target, we determine whether it can reach the current target from index i. This
78+
step allows us to use the jump range at each position to decide if it can potentially lead us to the end.
79+
80+
3. If the condition i + nums[i] >= target is TRUE, the current index i can jump far enough to reach the current target.
81+
In that case, we update target = i, effectively saying, “Now we just need to reach index i instead.” If the condition
82+
fails, we move back in the array one step further and try again with the previous index.
83+
We repeat this process until we either:
84+
- Successfully moving the target back to index 0 means the start of the array can reach the end. In this case, we
85+
return TRUE.
86+
- Or reach the start without ever being able to update the target to 0, which means there is no valid path. In this
87+
case, we return FALSE.
88+
89+
#### Solution Summary
90+
91+
1. Set the last index of the array as the target index.
92+
2. Traverse the array backward and verify if we can reach the target index from any of the previous indexes.
93+
- If we can reach it, we update the target index with the index that allows us to jump to the target index.
94+
- We repeat this process until we’ve traversed the entire array.
95+
3. Return TRUE if, through this process, we can reach the first index of the array. Otherwise, return FALSE.
96+
97+
#### Time Complexity
98+
99+
The time complexity of the above solution is O(n), since we traverse the array only once, where n is the number of
100+
elements in the array.
101+
102+
#### Space Complexity
103+
104+
The space complexity of the above solution is O(1), because we do not use any extra space.
105+
106+
107+
---
108+
109+
# Jump Game II
110+
111+
You are given a 0-indexed array of integers nums of length n. You are initially positioned at nums[0].
112+
113+
Each element nums[i] represents the maximum length of a forward jump from index i. In other words, if you are at
114+
nums[i], you can jump to any nums[i + j] where:
115+
116+
0 <= j <= nums[i] and
117+
i + j < n
118+
Return the minimum number of jumps to reach nums[n - 1]. The test cases are generated such that you can reach
119+
nums[n - 1].
120+
121+
```text
122+
Example 1:
123+
124+
Input: nums = [2,3,1,1,4]
125+
Output: 2
126+
Explanation: The minimum number of jumps to reach the last index is 2. Jump 1 step from index 0 to 1, then 3 steps to
127+
the last index.
128+
```
129+
130+
```text
131+
Example 2:
132+
133+
Input: nums = [2,3,0,1,4]
134+
Output: 2
135+
```
136+
137+
![Example 1](./images/examples/jump_game_2_example_1.png)
138+
![Example 2](./images/examples/jump_game_2_example_2.png)
139+
![Example 3](./images/examples/jump_game_2_example_3.png)
140+
![Example 4](./images/examples/jump_game_2_example_4.png)
141+
![Example 5](./images/examples/jump_game_2_example_5.png)
142+
![Example 6](./images/examples/jump_game_2_example_6.png)
143+
144+
## Solution
145+
146+
We’ll solve this problem using a greedy approach. At each step, we choose the jump that allows us to reach the farthest
147+
point. The objective is to minimize the number of jumps required to reach the end of the array. This strategy is
148+
considered greedy because it selects the best possible move at each step without considering the impact on future jumps.
149+
By always jumping as far as possible, we cover more distance and use fewer jumps to reach the end.
150+
151+
To find the minimum number of jumps needed to reach the end of the array, keep track of how far you can go with the
152+
current number of jumps. As you progress through the array, update this maximum reach based on your current position.
153+
When you reach the limit of your current jump range, increase your jump count and adjust the range to the farthest
154+
position you can reach with the next jump. Continue this process until you reach or exceed the end of the array.
155+
156+
The steps of the algorithm are given below:
157+
158+
1. Initialize three variables, all set to 0.
159+
- `jumps`: This variable tracks the minimum number of jumps required to reach the end of the array and will be
160+
returned as the final output.
161+
- `current_jump_boundary`: This represents the maximum index we can reach with the current number of jumps. It acts
162+
as the boundary of how far we can go before making another jump.
163+
- `farthest_jump_index`: This tracks the farthest index we can reach from the current position by considering all
164+
possible jumps within the current jump’s range.
165+
166+
2. Iterate through the nums array and perform the following steps:
167+
- Update `farthest_jump_index`: For each index i, calculate i + nums[i], which represents the farthest index we can
168+
reach from i. Update farthest_jump_index to be the maximum of its current value and i + nums[i].
169+
- Check if a new jump is needed: If i equals current_jump_boundary, it means we’ve reached the limit of the current
170+
jump. Increment jumps by 1, and update current_jump_boundary to farthest_jump_index to set up for the next jump.
171+
172+
3. After iterating through the array, jumps will contain the minimum number of jumps needed to reach the end. Return
173+
this value as the output.
174+
175+
Let’s look at the following illustration to get a better understanding of the solution:
176+
177+
![Solution 1](./images/solutions/jump_game_2_solution_1.png)
178+
![Solution 2](./images/solutions/jump_game_2_solution_2.png)
179+
![Solution 3](./images/solutions/jump_game_2_solution_3.png)
180+
![Solution 4](./images/solutions/jump_game_2_solution_4.png)
181+
![Solution 5](./images/solutions/jump_game_2_solution_5.png)
182+
![Solution 6](./images/solutions/jump_game_2_solution_6.png)
183+
![Solution 7](./images/solutions/jump_game_2_solution_7.png)
184+
![Solution 8](./images/solutions/jump_game_2_solution_8.png)
185+
![Solution 9](./images/solutions/jump_game_2_solution_9.png)
186+
![Solution 10](./images/solutions/jump_game_2_solution_10.png)
187+
![Solution 11](./images/solutions/jump_game_2_solution_11.png)
188+
![Solution 12](./images/solutions/jump_game_2_solution_12.png)
189+
![Solution 13](./images/solutions/jump_game_2_solution_13.png)
190+
191+
### Time Complexity
192+
193+
The time complexity of the above solution is O(n), where n is the length of nums because we are iterating the array once.
194+
195+
### Space Complexity
196+
197+
The space complexity of the above solution is O(1), because we are not using any extra space.
198+
199+
---
200+
201+
# Topics
202+
203+
- Array
204+
- Dynamic Programming
205+
- Greedy
Lines changed: 127 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,127 @@
1+
from typing import List
2+
3+
4+
def can_jump(nums: List[int]) -> bool:
5+
"""
6+
This function checks if it is possible to reach the last index of the array from the first index.
7+
Args:
8+
nums(list): list of integers
9+
Returns:
10+
bool: True if can jump to the last index, False otherwise
11+
"""
12+
13+
current_position = nums[0]
14+
15+
for idx in range(1, len(nums)):
16+
if current_position == 0:
17+
return False
18+
19+
current_position -= 1
20+
21+
current_position = max(current_position, nums[idx])
22+
23+
return True
24+
25+
26+
def can_jump_2(nums: List[int]) -> bool:
27+
"""
28+
This function checks if it is possible to reach the last index of the array from the first index.
29+
30+
This variation starts checking from the last element in the input list and tracking back to check if it is possible
31+
to reach the end.
32+
33+
Args:
34+
nums(list): list of integers
35+
Returns:
36+
bool: True if can jump to the last index, False otherwise
37+
"""
38+
target_num_index = len(nums) - 1
39+
40+
for idx in range(len(nums) - 2, -1, -1):
41+
if target_num_index <= idx + nums[idx]:
42+
target_num_index = idx
43+
44+
if target_num_index == 0:
45+
return True
46+
return False
47+
48+
49+
def jump(nums: List[int]) -> int:
50+
"""
51+
This function returns the minimum number of jumps needed to reach the last index of the array from the first index.
52+
Args:
53+
nums(list): list of integers
54+
Returns:
55+
int: minimum number of jumps needed to reach the last index
56+
"""
57+
size = len(nums)
58+
# if start index == destination index == 0
59+
if size == 1:
60+
return 0
61+
62+
# destination is last index
63+
destination = size - 1
64+
65+
# record of current coverage, record of last jump index
66+
current_coverage, last_jump_index = 0, 0
67+
68+
# min number of jumps
69+
min_jumps = 0
70+
71+
# Greedy strategy: extend coverage as long as possible with lazy jump
72+
for idx in range(size):
73+
# extend coverage as far as possible
74+
current_coverage = max(current_coverage, idx + nums[idx])
75+
76+
# forced to jump (by lazy jump) to extend coverage
77+
if idx == last_jump_index:
78+
# update last jump index
79+
last_jump_index = current_coverage
80+
81+
# update counter of jump by +1
82+
min_jumps += 1
83+
84+
# check if destination has been reached
85+
if current_coverage >= destination:
86+
return min_jumps
87+
88+
return min_jumps
89+
90+
91+
def jump_2(nums: List[int]) -> int:
92+
"""
93+
This function returns the minimum number of jumps needed to reach the last index of the array from the first index.
94+
Args:
95+
nums(list): list of integers
96+
Returns:
97+
int: minimum number of jumps needed to reach the last index
98+
"""
99+
# Store the length of the input array
100+
size = len(nums)
101+
102+
# if start index == destination index == 0
103+
if size == 1:
104+
return 0
105+
106+
# Initialize the variables to track the number of jumps,
107+
# the current jump's limit, and the farthest reachable index
108+
min_jumps = 0
109+
current_jump_boundary = 0
110+
furthest_jump_index = 0
111+
112+
# Iterate through the array, stopping before the last element
113+
for idx in range(size - 1):
114+
# Update the farthest_jump_index to be the maximum of its current value
115+
# and the index we can reach from the current position
116+
furthest_jump_index = max(furthest_jump_index, idx + nums[idx])
117+
118+
# If we have reached the limit of the current jump
119+
if idx == current_jump_boundary:
120+
# update counter of jump by +1
121+
min_jumps += 1
122+
123+
# Update the current_jump_boundary to the farthest we can reach
124+
current_jump_boundary = furthest_jump_index
125+
126+
# Return the total number of jumps needed to reach the end of the array
127+
return min_jumps
72.5 KB
Loading
71.6 KB
Loading
65.6 KB
Loading
76.6 KB
Loading
77.8 KB
Loading
69 KB
Loading
45.3 KB
Loading

0 commit comments

Comments
 (0)