mirror of
https://github.moeyy.xyz/https://github.com/trekhleb/javascript-algorithms.git
synced 2024-12-26 07:01:18 +08:00
Add Jump Game.
This commit is contained in:
parent
549759757d
commit
3e8540beac
@ -116,6 +116,7 @@ a set of rules that precisely define a sequence of operations.
|
||||
* **Uncategorized**
|
||||
* `B` [Tower of Hanoi](src/algorithms/uncategorized/hanoi-tower)
|
||||
* `B` [Square Matrix Rotation](src/algorithms/uncategorized/square-matrix-rotation) - in-place algorithm
|
||||
* `B` [Jump Game](src/algorithms/uncategorized/jump-game) - backtracking, dynamic programming (top-down + bottom-up) and greedy examples
|
||||
* `A` [N-Queens Problem](src/algorithms/uncategorized/n-queens)
|
||||
* `A` [Knight's Tour](src/algorithms/uncategorized/knight-tour)
|
||||
|
||||
@ -129,6 +130,7 @@ algorithm is an abstraction higher than a computer program.
|
||||
* `A` [Maximum Subarray](src/algorithms/sets/maximum-subarray)
|
||||
* `A` [Travelling Salesman Problem](src/algorithms/graph/travelling-salesman) - shortest possible route that visits each city and returns to the origin city
|
||||
* **Greedy** - choose the best option at the current time, without any consideration for the future
|
||||
* `B` [Jump Game](src/algorithms/uncategorized/jump-game)
|
||||
* `A` [Unbound Knapsack Problem](src/algorithms/sets/knapsack-problem)
|
||||
* `A` [Dijkstra Algorithm](src/algorithms/graph/dijkstra) - finding shortest path to all graph vertices
|
||||
* `A` [Prim’s Algorithm](src/algorithms/graph/prim) - finding Minimum Spanning Tree (MST) for weighted undirected graph
|
||||
@ -142,10 +144,12 @@ algorithm is an abstraction higher than a computer program.
|
||||
* `B` [Quicksort](src/algorithms/sorting/quick-sort)
|
||||
* `B` [Tree Depth-First Search](src/algorithms/tree/depth-first-search) (DFS)
|
||||
* `B` [Graph Depth-First Search](src/algorithms/graph/depth-first-search) (DFS)
|
||||
* `B` [Jump Game](src/algorithms/uncategorized/jump-game)
|
||||
* `A` [Permutations](src/algorithms/sets/permutations) (with and without repetitions)
|
||||
* `A` [Combinations](src/algorithms/sets/combinations) (with and without repetitions)
|
||||
* **Dynamic Programming** - build up a solution using previously found sub-solutions
|
||||
* `B` [Fibonacci Number](src/algorithms/math/fibonacci)
|
||||
* `B` [Jump Game](src/algorithms/uncategorized/jump-game)
|
||||
* `A` [Levenshtein Distance](src/algorithms/string/levenshtein-distance) - minimum edit distance between two sequences
|
||||
* `A` [Longest Common Subsequence](src/algorithms/sets/longest-common-subsequence) (LCS)
|
||||
* `A` [Longest Common Substring](src/algorithms/string/longest-common-substring)
|
||||
@ -159,6 +163,7 @@ algorithm is an abstraction higher than a computer program.
|
||||
* **Backtracking** - similarly to brute force, try to generate all possible solutions, but each time you generate next solution you test
|
||||
if it satisfies all conditions, and only then continue generating subsequent solutions. Otherwise, backtrack, and go on a
|
||||
different path of finding a solution. Normally the DFS traversal of state-space is being used.
|
||||
* `B` [Jump Game](src/algorithms/uncategorized/jump-game)
|
||||
* `A` [Hamiltonian Cycle](src/algorithms/graph/hamiltonian-cycle) - Visit every vertex exactly once
|
||||
* `A` [N-Queens Problem](src/algorithms/uncategorized/n-queens)
|
||||
* `A` [Knight's Tour](src/algorithms/uncategorized/knight-tour)
|
||||
|
128
src/algorithms/uncategorized/jump-game/README.md
Normal file
128
src/algorithms/uncategorized/jump-game/README.md
Normal file
@ -0,0 +1,128 @@
|
||||
# Jump Game
|
||||
|
||||
## The Problem
|
||||
|
||||
Given an array of non-negative integers, you are initially positioned at
|
||||
the first index of the array. Each element in the array represents your maximum
|
||||
jump length at that position.
|
||||
|
||||
Determine if you are able to reach the last index.
|
||||
|
||||
**Example #1**
|
||||
|
||||
```
|
||||
Input: [2,3,1,1,4]
|
||||
Output: true
|
||||
Explanation: Jump 1 step from index 0 to 1, then 3 steps to the last index.
|
||||
```
|
||||
|
||||
**Example #2**
|
||||
|
||||
```
|
||||
Input: [3,2,1,0,4]
|
||||
Output: false
|
||||
Explanation: You will always arrive at index 3 no matter what. Its maximum
|
||||
jump length is 0, which makes it impossible to reach the last index.
|
||||
```
|
||||
|
||||
## Naming
|
||||
|
||||
We call a position in the array a **"good index"** if starting at that position,
|
||||
we can reach the last index. Otherwise, that index is called a **"bad index"**.
|
||||
The problem then reduces to whether or not index 0 is a "good index".
|
||||
|
||||
## Solutions
|
||||
|
||||
### Approach 1: Backtracking
|
||||
|
||||
This is the inefficient solution where we try every single jump pattern that
|
||||
takes us from the first position to the last. We start from the first position
|
||||
and jump to every index that is reachable. We repeat the process until last
|
||||
index is reached. When stuck, backtrack.
|
||||
|
||||
> See [backtrackingJumpGame.js](backtrackingJumpGame.js) file
|
||||
|
||||
**Time complexity:**: `O(2^n)`.
|
||||
There are 2<sup>n</sup> (upper bound) ways of jumping from
|
||||
the first position to the last, where `n` is the length of
|
||||
array `nums`.
|
||||
|
||||
**Auxiliary Space Complexity**: `O(n)`.
|
||||
Recursion requires additional memory for the stack frames.
|
||||
|
||||
### Approach 2: Dynamic Programming Top-down
|
||||
|
||||
Top-down Dynamic Programming can be thought of as optimized
|
||||
backtracking. It relies on the observation that once we determine
|
||||
that a certain index is good / bad, this result will never change.
|
||||
This means that we can store the result and not need to recompute
|
||||
it every time.
|
||||
|
||||
Therefore, for each position in the array, we remember whether the
|
||||
index is good or bad. Let's call this array memo and let its values
|
||||
be either one of: GOOD, BAD, UNKNOWN. This technique is
|
||||
called memoization.
|
||||
|
||||
> See [dpTopDownJumpGame.js](dpTopDownJumpGame.js) file
|
||||
|
||||
**Time complexity:**: `O(n^2)`.
|
||||
For every element in the array, say `i`, we are looking at the
|
||||
next `nums[i]` elements to its right aiming to find a GOOD
|
||||
index. `nums[i]` can be at most `n`, where `n` is the length
|
||||
of array `nums`.
|
||||
|
||||
**Auxiliary Space Complexity**: `O(2 * n) = O(n)`.
|
||||
First `n` originates from recursion. Second `n` comes from the
|
||||
usage of the memo table.
|
||||
|
||||
### Approach 3: Dynamic Programming Bottom-up
|
||||
|
||||
Top-down to bottom-up conversion is done by eliminating recursion.
|
||||
In practice, this achieves better performance as we no longer have the
|
||||
method stack overhead and might even benefit from some caching. More
|
||||
importantly, this step opens up possibilities for future optimization.
|
||||
The recursion is usually eliminated by trying to reverse the order of
|
||||
the steps from the top-down approach.
|
||||
|
||||
The observation to make here is that we only ever jump to the right.
|
||||
This means that if we start from the right of the array, every time
|
||||
we will query a position to our right, that position has already be
|
||||
determined as being GOOD or BAD. This means we don't need to recurse
|
||||
anymore, as we will always hit the memo table.
|
||||
|
||||
> See [dpBottomUpJumpGame.js](dpBottomUpJumpGame.js) file
|
||||
|
||||
**Time complexity:**: `O(n^2)`.
|
||||
For every element in the array, say `i`, we are looking at the
|
||||
next `nums[i]` elements to its right aiming to find a GOOD
|
||||
index. `nums[i]` can be at most `n`, where `n` is the length
|
||||
of array `nums`.
|
||||
|
||||
**Auxiliary Space Complexity**: `O(n)`.
|
||||
This comes from the usage of the memo table.
|
||||
|
||||
### Approach 4: Greedy
|
||||
|
||||
Once we have our code in the bottom-up state, we can make one final,
|
||||
important observation. From a given position, when we try to see if
|
||||
we can jump to a GOOD position, we only ever use one - the first one.
|
||||
In other words, the left-most one. If we keep track of this left-most
|
||||
GOOD position as a separate variable, we can avoid searching for it in
|
||||
the array. Not only that, but we can stop using the array altogether.
|
||||
|
||||
> See [greedyJumpGame.js](greedyJumpGame.js) file
|
||||
|
||||
**Time complexity:**: `O(n)`.
|
||||
We are doing a single pass through the `nums` array, hence `n` steps,
|
||||
where `n` is the length of array `nums`.
|
||||
|
||||
**Auxiliary Space Complexity**: `O(1)`.
|
||||
We are not using any extra memory.
|
||||
|
||||
## References
|
||||
|
||||
- [Jump Game Fully Explained on LeetCode](https://leetcode.com/articles/jump-game/)
|
||||
- [Dynamic Programming vs Divide and Conquer](https://itnext.io/dynamic-programming-vs-divide-and-conquer-2fea680becbe)
|
||||
- [Dynamic Programming](https://en.wikipedia.org/wiki/Dynamic_programming)
|
||||
- [Memoization on Wikipedia](https://en.wikipedia.org/wiki/Memoization)
|
||||
- [Top-Down and Bottom-Up Design on Wikipedia](https://en.wikipedia.org/wiki/Top-down_and_bottom-up_design)
|
Loading…
Reference in New Issue
Block a user