55 跳跃游戏(递归、动态规划、贪心)

1. 问题描述:

给定一个非负整数数组,你最初位于数组的第一个位置。

数组中的每个元素代表你在该位置可以跳跃的最大长度。

判断你是否能够到达最后一个位置。

示例 1:

输入: [2,3,1,1,4]
输出: true
解释: 我们可以先跳 1 步,从位置 0 到达 位置 1, 然后再从位置 1 跳 3 步到达最后一个位置。
示例 2:

输入: [3,2,1,0,4]
输出: false
解释: 无论怎样,你总会到达索引为 3 的位置。但该位置的最大跳跃长度是 0 , 所以你永远不可能到达最后一个位置。

来源:力扣(LeetCode)
链接:https://leetcode-cn.com/problems/jump-game

2. 思路分析:

① 之前我也写过一篇关于这个题目的博客,使用的是bfs宽度优先搜索来解决的,在领扣中发现几乎没有人使用这个思路,包括官方提供的题解中也没有使用bfs解决的代码,使用比较多的是递归回溯、动态规划与贪心这三种解法,在官方提供的这些解法中都是很值得我们去学习的,非常有价值,对于我们遇到这一类问题怎么样求解非常有帮助,需要好好理解一下,下面是我对于官方题解给出的方法的自己的理解

② 第一种方法是使用递归,这个相对来说比较容易想到,因为很明显题目告诉我们每个位置最多可以向右跳nums[i]步,所以我们从起点出发,尝试从当前的位置跳到其他的位置,尝试的次数最多为nums[i],所以可以使用递归来求解,尝试每一个位置能否跳到最后的位置,假如中间求解的过程中发现可以到达最后的位置那么直接在中间过程中返回即可,这也是与之前返回值的递归不同的一点,之前是需要尝试求解出所有的答案才能够最终走完这个的递归过程,这里可以发现中间有一个能够到达那么直接返回,因为求解的问题不同,所有处理方法会有所差别,但是大体上是一样的

③ 第二种方法使用的是记忆型的递归来求解的,主要是借助于递归尝试的思路,只是在这里使用了一个数组来记录中间求解过的结果,与我们之前的记忆型的递归本质上是一样的,假如发现之前已经求解过了那么直接返回之前求解过的值,使用enum类型的三种标记方法来表示当前的位置能否跳到最后一个坐标,整个过程还是比较好理解的,官方称之为自顶向下的递归求解

④ 第三种方法与第二种方法的思路类似,这里使用的是自底向上进行递归,从倒数第二个位置出发,看一下能否到达最后一个位置,假如能够标记这个位置为好位置,假如不能够那么标记这个位置为坏位置,然后继续往前面的位置进行尝试,看一下当前这个位置能否到达之前标记过的好位置,一直这样往前进行尝试,最后看一下第一个位置是否是好位置即可,因为假如第一个位置是好位置那么在往右边走的时候肯定可以到达之前标记过的好位置,所以最后判断一下最开始的位置即可

⑤ 第四种方法是贪心策略,思路比较好理解但是比较难想出来,还是从右边的倒数第二个位置进行尝试,看一下当前位置加上能够跳的最大距离能否大于等于最后一个位置假如能够到达说明当前位置是好位置,这个时候更新左侧位置,在往前尝试的过程直到第一个位置,其实整个思路还是比较好理解的

3. 官方提供的代码如下:

递归代码:

public class Solution {
    public boolean canJumpFromPosition(int position, int[] nums) {
        if (position == nums.length - 1) {
            return true;
        }
        int furthestJump = Math.min(position + nums[position], nums.length - 1);
        for (int nextPosition = position + 1; nextPosition <= furthestJump; nextPosition++) {
            if (canJumpFromPosition(nextPosition, nums)) {
                return true;
            }
        }
        return false;
    }

    public boolean canJump(int[] nums) {
        return canJumpFromPosition(0, nums);
    }
}

记忆型递归:

enum Index {
    GOOD, BAD, UNKNOWN
}

public class Solution {
    Index[] memo;
    public boolean canJumpFromPosition(int position, int[] nums) {
        if (memo[position] != Index.UNKNOWN) {
            return memo[position] == Index.GOOD ? true : false;
        }

        int furthestJump = Math.min(position + nums[position], nums.length - 1);
        for (int nextPosition = position + 1; nextPosition <= furthestJump; nextPosition++) {
            if (canJumpFromPosition(nextPosition, nums)) {
                memo[position] = Index.GOOD;
                return true;
            }
        }
        memo[position] = Index.BAD;
        return false;
    }

    public boolean canJump(int[] nums) {
        memo = new Index[nums.length];
        for (int i = 0; i < memo.length; i++) {
            memo[i] = Index.UNKNOWN;
        }
        memo[memo.length - 1] = Index.GOOD;
        return canJumpFromPosition(0, nums);
    }
}

动态规划:

enum Index {
    GOOD, BAD, UNKNOWN
}

public class Solution {
    public boolean canJump(int[] nums) {
        Index[] memo = new Index[nums.length];
        for (int i = 0; i < memo.length; i++) {
            memo[i] = Index.UNKNOWN;
        }
        memo[memo.length - 1] = Index.GOOD;
        for (int i = nums.length - 2; i >= 0; i--) {
            int furthestJump = Math.min(i + nums[i], nums.length - 1);
            for (int j = i + 1; j <= furthestJump; j++) {
                if (memo[j] == Index.GOOD) {
                    memo[i] = Index.GOOD;
                    break;
                }
            }
        }

        return memo[0] == Index.GOOD;
    }
}

贪心:

public class Solution {
    public boolean canJump(int[] nums) {
        int lastPos = nums.length - 1;
        for (int i = nums.length - 1; i >= 0; i--) {
            if (i + nums[i] >= lastPos) {
                lastPos = i;
            }
        }
        return lastPos == 0;
    }
}
发布了569 篇原创文章 · 获赞 153 · 访问量 59万+

猜你喜欢

转载自blog.csdn.net/qq_39445165/article/details/105367829