leetcode-045-跳跃游戏2

文章提供了一个Java程序,解决跳跃游戏II的问题,即给定一个数组,每一步可以从当前位置跳向最大长度内的任何位置,目标是最小跳跃次数到达数组末尾。解法包括两种:一种是动态规划,另一种是贪心算法。程序中包含两种方法的实现,并有测试用例进行验证,展示了算法的时间复杂度和性能。
摘要由CSDN通过智能技术生成

题目及测试

package pid045;
/*45. 跳跃游戏 II
给定一个长度为 n 的 0 索引整数数组 nums。初始位置为 nums[0]。

每个元素 nums[i] 表示从索引 i 向前跳转的最大长度。换句话说,如果你在 nums[i] 处,你可以跳转到任意 nums[i + j] 处:

0 <= j <= nums[i] 
i + j < n
返回到达 nums[n - 1] 的最小跳跃次数。生成的测试用例可以到达 nums[n - 1]。

 

示例 1:

输入: nums = [2,3,1,1,4]
输出: 2
解释: 跳到最后一个位置的最小跳跃数是 2。
     从下标为 0 跳到下标为 1 的位置,跳 1 步,然后跳 3 步到达数组的最后一个位置。
示例 2:

输入: nums = [2,3,0,1,4]
输出: 2
 

提示:

1 <= nums.length <= 104
0 <= nums[i] <= 1000
题目保证可以到达 nums[n-1]
*/
public class main {
	
	public static void main(String[] args) {
		int[][] testTable = {{2,3,1,1,4},{2,3,0,1,4},{1,2,3,5},{1,1,1,1}};
		for (int[] ito : testTable) {
			test(ito);
		}
	}
		 
	private static void test(int[] ito) {
		Solution solution = new Solution();
		int rtn;
		long begin = System.currentTimeMillis();
		for (int i = 0; i < ito.length; i++) {
		    System.out.print(ito[i]+" ");
		}//开始时打印数组
		rtn = solution.jump(ito);//执行程序
		long end = System.currentTimeMillis();		
		System.out.println("rtn=" + rtn);
		/*for (int i = 0; i < rtn; i++) {
		    System.out.print(ito[i]+" ");
		}//打印结果几数组
*/		System.out.println();
		System.out.println("耗时:" + (end - begin) + "ms");
		System.out.println("-------------------");
	}

}

解法1(成功,366ms,极慢)

package pid045;

class Solution {
    public int jump(int[] nums) {
    	int length = nums.length;
    	if(length<=2){
    		return length -1;
    	}
    	int[] result = new int[length];
    	result[0]=0;
    	result[1]=1;
    	for(int i=2;i<length;i++){
    		int min = Integer.MAX_VALUE;
    		for(int j=0;j<i;j++){
    			if(j+nums[j]>=i){
    				min = Math.min(min, result[j]+1);
    			}
    		}
    		result[i] = min;
    	}
    	return result[length-1];
    }
}

解法2(别人的)

为了方便理解,我们将「起点」考虑进去。

由于起点是一定要经过的,因此,我们从起点开始跳,并初始化为第一步 step = 1,同时能得到从起点到达的最远距离,我们将其设置为边界 end,因为「我们不管起跳的方案是什么,我们只要每次贪心地能跳到最远即可」,因此,我们从左到右遍历每个能到达的地点,只要当前点没到边界 end,说明我们还在第一步的起跳范围内,我们就不起跳,步数还是原来的步数 1,同时,在到达第一步的边界 end 之前,我们就继续贪心地找到第二点到边界 end 之间能够到达的最远距离作为第二步的边界,用 nextMaxEnd 表示,表示未来第二步的边界(因为第二步一定是第二个点到边界 end 之间的某一个点,所以我们只能在这之间更新 nextMaxEnd);

遍历途中,一旦当前点来到了第一步的边界 end,说明第一步能够到达的最远距离已经走到了,必须跳第二步了 (注意这里并不是真正的起跳点,只是已经到达了第一步能到达的最远距离而已,无论如何你都必须跳出第二步了,我们只是贪心地每次都到最远距离了才肯跳出下一步,但一定要记住,这并不是真正的起跳方案),此时就把边界 end 更新为 nextMaxEnd 表示第二步的边界,并且 step++,表示你无论如何都得跳出第二步了,后面就是在下一个点到 end 之间更新第三步能够到达的最远距离即可,到达第二步的 end 时,继续更新 end 为 nextMaxEnd,step++;

以此类推,不断地在到达当前步的边界 end 前更新下一步能够到达的最远距离nextMaxEnd 作为下一步的边界,更新途中一旦到达当前步的边界 end,就表示当前步已经贪心地走完了,必须跳下一步了,step++,并且下一步能够到达的最远边界我们已经在之前比较出来了,直接更新即可:end = nextMaxEnd。

到这里,其实就可以明白为什么不用遍历终点了,因为边界 end 有可能是终点,那 end 一旦到了终点其实也就到了,但是我们的代码逻辑是到达 end 就得步数加一,其实这是没必要的。

下面的代码和官方的有一些不同,我是先初始化了第一步,比较好理解。

class Solution {
    public int jump(int[] nums) {
        if(nums.length == 1) return 0;

        // 下面三行代码初始化第一步
        int step = 1;       // 第一步
        int end = nums[0];  // 第一步的边界,也就是第一步能到达的最远距离
        int nextMaxEnd = 0; // 下一步能到达的最远边界 (只要当前步到达它的边界 end,就更新为 nextMaxEnd 作为下一步的边界 end)

        for(int i = 1; i < nums.length-1; i++) {
            nextMaxEnd = Math.max(nextMaxEnd, nums[i]+i);
            if(i == end) {
                end = nextMaxEnd;
                step++;
            }
        }

        return step;
    }
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值