算法的重要性,我就不多说了吧,想去大厂,就必须要经过基础知识和业务逻辑面试+算法面试。所以,为了提高大家的算法能力,这个公众号后续每天带大家做一道算法题,题目就从LeetCode上面选 !
今天和大家聊的问题叫做 跳跃游戏,我们先来看题面:
https://leetcode-cn.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.
给定一个非负整数数组,你最初位于数组的第一个位置。
数组中的每个元素代表你在该位置可以跳跃的最大长度。
判断你是否能够到达最后一个位置。
示例 1:
输入: [2,3,1,1,4]
输出:
true
解释: 我们可以先跳
1 步,从位置
0 到达 位置
1, 然后再从位置
1 跳
3 步到达最后一个位置。
示例 2:
输入: [3,2,1,0,4]
输出:
false
解释: 无论怎样,你总会到达索引为
3 的位置。但该位置的最大跳跃长度是
0 , 所以你永远不可能到达最后一个位置。
回溯法
回溯法的思想很简单,寻找到所有能到达数组的最后一个位置的可能路径,计算其最短值即可。由于是穷举,其时间复杂度是很高的,达到了O(nums[0] + nums[1] + nums[2] + ... + nums[n - 1])级别,其中n为nums数组的长度。而空间复杂度则是递归深度,是O(n)级别的。
public class Solution {
int steps;
public int jump(int[] nums) {
int n = nums.length;
steps = n -
1;
jump(nums,
0,
0);
return steps;
}
private void jump(int[] nums, int index, int tempSteps) {
if(index >= nums.length -
1) {
if(index == nums.length -
1) {
steps = Math.min(steps, tempSteps);
}
return;
}
for (int i =
1; i <= nums[index]; i++) {
jump(nums, index + i, tempSteps +
1);
}
}
}
动态规划法
状态定义:f(x, y)--------表示从索引x,走到索引y的最短步数
状态转移:
(1)如果nums[x] + x >= y,说明一步就可以从索引x走到索引y,f(x, y) = 1。
(2)如果nums[x] + x < y,f(x, y) = 1 + min{f(x + 1,y), f(x + 2, y), ... , f(x + nums[x], y)}。
时间复杂度和空间复杂度都是O(n ^ 2)级别的。
public class Solution {
public int jump(int[] nums) {
int n = nums.length;
if(n == 1) {
return 0;
}
int[][] steps = new int[n][n];
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
steps[i][j] = n - 1;
}
}
for (int i = 0; i < n; i++) {
steps[i][i] = 0;
}
for (int i = 0; i >= 1 - n; i--) {
for (int j = 0; j < n + i; j++) {
if(nums[j] + j >= j - i) {
steps[j][j - i] = 1;
}else {
for (int k = 1; k <= nums[j]; k++) {
steps[j][j - i] = Math.min(steps[j][j - i], 1 + steps[j + k][j - i]);
}
}
}
}
return steps[0][n - 1];
}
}
贪心算法
前面两种方法:回溯法和动态规划都会提前超时。那么必然就得用贪心算法了。贪心算法的本质是在动态规划的基础上舍弃一些不可能的情况,类似于回溯算法的剪枝过程。
对于本题而言,假设我们现在在索引i的位置。
如果索引i的值为0,那么我们不可能再继续前进了,这种情况舍弃。
如果索引i的值不为0,那么我们下一步可以走到索引i + k(1 <= k <= nums[i])。而在索引i + k我们又可以走到索引i + k + p(1 <= p <= nums[i + k]),我们选取索引i + k的原则是i + k + p取得最大值。
public class Solution {
public int jump(int[] nums) {
int n = nums.length;
int steps = 0;
int index = 0;
while(index < n - 1) {
steps++;
int[] lengths = new int[nums[index]];
if(index + nums[index] >= n - 1) {
break;
}
for (int i = index + 1; i <= index + nums[index]; i++) {
lengths[i - index - 1] = i + nums[i];
}
int max = 0;
for (int i = 0; i < lengths.length; i++) {
if(lengths[i] > lengths[max]) {
max = i;
}
}
index = max + index + 1;
}
return steps;
}
}
好了,今天的文章就到这里,如果觉得有所收获,请顺手点个在看或者转发吧,你们的支持是我最大的动力。
上期推文:
LeetCode1-50题汇总,速度收藏!
LeetCode刷题实战51:N 皇后
LeetCode刷题实战52:N皇后 II
LeetCode刷题实战53:最大子序和
LeetCode刷题实战54:螺旋矩阵