说明
算法:Jump Game
LeetCode地址:https://leetcode.com/problems/jump-game/
题目:
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.
解题思路1 – 回溯up –> bottom
问题描述:遍历当前位置i,加上当前位置的值为最大可以跨越的步数,
int maxPosition = i + nums[i];
也就是可以跨越的步数为,0~maxPosition。
这明显可以用回溯的方法解决,并cache已经知道结果。
先从上到下遍历,并每次都是到达最大数,这样子可以达到最快速。
时间复杂度为O(n^2)
代码 – 回溯up –> bottom
public static boolean canJump(int[] nums) {
// result memory, 1 is true; -1 is false;
int[] memo = new int[nums.length + 1];
return helper(0, nums, memo);
}
public static boolean helper(int position, int[] nums, int[] memo) {
if (memo[position] == 1 || position == nums.length - 1) {
return true;
} else if (memo[position] == -1) {
return false;
}
int maxPosition = Math.min(position + nums[position], nums.length - 1) ;
for(int i = maxPosition; i > position; i--) {
memo[i] = helper(i, nums, memo) ? 1 : -1;
if(memo[i] == 1) {
return true;
}
}
return false;
}
解题思路2- 回溯 bottom–> up
从前往后遍历的弊端为,不知道终点在哪里,做了很多无用功, 比如前面cache虽然有记录,当时不是最优解。
从后往前,cache命中率更高,并且大数的命中,直接标识小数可达。
时间复杂度为O(n^2)
代码 – 回溯 bottom–> up
public static boolean canJumpWithBottomUp(int[] nums) {
// result memory, 1 is true; -1 is false;
int[] memo = new int[nums.length];
// check if the length == 1
memo[nums.length - 1] = 1;
for(int i = nums.length -2; i >= 0; i--) {
int maxPosition = Math.min(i + nums[i], nums.length - 1);
for(int k = i + 1; k <= maxPosition; k++) {
if (k == nums.length - 1 || memo[k] == 1) {
memo[i] = 1;
break;
}
}
}
return memo[0] == 1;
}
解题思路3- Greedy
从后往前遍历,如果遇到
i+nums[i] >= lastIndex;
, 直接修改
lastIndex = 1;
时间复杂度为O(n)
代码 – Greedy
public static boolean canJumpWithGreedy(int[] nums) {
int lastIndex = nums.length - 1;
for (int i = lastIndex - 1; i >=0 ; i--) {
if (i + nums[i] >= lastIndex) {
lastIndex = i;
}
}
return lastIndex == 0;
}
测试方法
public static void main(String[] args) {
int[] input = {2,3,1,1,4};
int[] input1 = {3,2,1,0,4};
//System.out.println("intput: " + Arrays.toString(input) + '\n' + "output: " + canJump(input));
//System.out.println("input1: " + Arrays.toString(input1) + '\n' + "output: " + canJump(input1));
//System.out.println("intput: " + Arrays.toString(input) + '\n' + "output: " + canJumpWithBottomUp(input));
//System.out.println("input1: " + Arrays.toString(input1) + '\n' + "output: " + canJumpWithBottomUp(input1));
System.out.println("intput: " + Arrays.toString(input) + '\n' + "output: " + canJumpWithGreedy(input));
System.out.println("input1: " + Arrays.toString(input1) + '\n' + "output: " + canJumpWithGreedy(input1));
}
运行结果
intput: [2, 3, 1, 1, 4]
output: true
input1: [3, 2, 1, 0, 4]
output: false
总结
惯性思维用回溯,最后发现用贪心算法最优。
代码下载:
https://github.com/zgpeace/awesome-java-leetcode/blob/master/code/LeetCode/src/popular/JumpGame.java