算法训练#2:跳跃游戏 II

47 阅读3分钟

“开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 4 天,点击查看活动详情

45. 跳跃游戏 II - 力扣(LeetCode)

题目内容

image.png

思路方法一

用最简单粗暴的想法,就是使用循环遍历数组,把第i(i>\gt1)次能跳到的地方都找出来,如果能跳到最后的话就把次数记下来,所有的次数最小的即为答案。
但本人在思考的过程中发现,若使用广度优先搜索方法来实现这个思路会更加简便。
这道题目求解的暴力思路可以看作一棵不规则树的寻求路径过程。例如,树根节点就是起点的下标,在起点选择走多少步,根节点就会有多少个分支,第二层的节点就是起点选择完步数走到的数组下标,其他的节点以此类推分支出其他子树。
使用广度优先搜索一旦搜到解就可以马上结束搜索过程返回答案,因为广度优先搜索的层数就是跳跃的次数。

代码实现

这代码超时了,元素上万的时候超的,证明思路不够好,放在这里只是参考

class Solution {
    //这里使用队列实现广度优先搜索
    Queue<Integer> forward=new LinkedList<>();
    public int jump(int[] nums) {
        int count=0;//计步器
        
        forward.add(0);//根节点
        
        forward.add(Integer.MAX_VALUE);//每一层节点后面加个不可能取到的值加以区分
        
        while(!forward.isEmpty()){//队空即表示遍历完成
        
            int x=forward.remove();//遍历队列,队头节点出队
            
            if(x!=Integer.MAX_VALUE) {
                if(x== nums.length-1){//遍历到题解,循环结束
                    break;
                }
                for (int i = 1; i <= nums[x]; i++)//遍历到一般节点
                    if(!forward.contains(x+i)) {//防止加入重复下标,增加计算量
                        forward.add(x + i);加下一层的节点入队
                    }
            }else {
                count++;
                //防止把树都遍历完了还在往队列里加节点
                //(题目的值都可以遍历到终点但我们要养成使代码能够面对各种情况的意识)
                if(!forward.isEmpty())
                    forward.add(Integer.MAX_VALUE);
            }
        }
        return count;
    }
}

思路方法二

用动态规划的思想去分解这个题目,会简单许多。下面找出动态规划的三个重要元素进而使用代码实现。

最后一步

这里的最后一步就是跳到数组最右边那一步,分解为子问题就是跳到下标为i的那一步

转移方程

这里的转移方程需要知道所有用于比较的东西 由于从尾部回溯会比较复杂,所以这道题我们选择从头部开始遍历。
f(i)f(i)为到 i 所需要的最小的步数
那么,我们就可以知道,在从前往后的遍历中,跳跃到后面某个下标(这里设为 x )的某条路径会先被遍历出来(不一定是最优),可以先把这条路径对应的次数先暂时存入f(x)f(x)中,后面再有到 x 的路径的对应次数再与原先存入的进行比较,最后得出fnums.length1f(nums.length-1)(答案)的最佳值
用数学语言表达出来就是 f(i+j)=min(f(i)+1,f(i+j))f(i+j)=min(f(i)+1,f(i+j))(i为遍历到的数组的下标,i+j与上方的x对应,j则为nums[i]nums[i]的值)

边界条件

由题意可知,数组的值是不规则的,我们无法直接赋值其他下标,边界条件只能是 f(0)f(0),而f(0)=0f(0)=0,同时为了保证转移方程能够正常运行,我们需要把其他位置设置为整型最大值或者一个比题目设置取值范围更大的值。

代码实现

class Solution { 
    public int jump(int[] nums) {
        int n=nums.length;
        int[] dp=new int[n];
        //特殊情况快速解决
        if(n==1) 
            return 0;
        if(n==2)
            return 1;
        //边界条件
        dp[0]=0;
        for(int i=1;i<n;i++)
            dp[i]=Integer.MAX_VALUE;
        
        for(int i=0;i<n;i++) {
            for(int j=1;j<=nums[i];j++) {
                if(i+j<nums.length)
                    dp[i+j]=Math.min(dp[i]+1,dp[i+j]);
            } 
        } 
        return dp[n-1];
    } 
}