题目地址:LeetCode LCP 09. 最小跳跃次数
首先理解题意:有N个小球从[0]到[n-1]排序,然后每个小球都可以跳跃到当前位置记录的值上,同时可以任意的跳跃到左边,当然0的时候只能向右跳跃。例如:
jump = [2, 5, 1, 1, 1, 1] ,当小球在0的位置上时,只能够向右跳2位即达到jump[2]上当到达a[2]的时候就可以左右横跳了,当然只能向右一位。
这个时候我们可以建立四个条件
第一个是:
int rightIdx = jump[curIdx]+curIdx;
第二个是
当curIdx >0 ,存在leftIdx
for(int i = 0 ;i<curIdx;i++){leftIdx = i}
第三个退出条件
if(jump[curIdx]+curIdx >= jump.length)
第四个条件,跳跃次数
int count = 0;
//当向左向右跳的时候需要count++
因为当跳到右边的时候,左边可以任意跳,当左边已经访问过的时候可以不需要再访问左边因此我们用一个数组visted[]是否为0表示是否已经访问过,然后用一个队列保存已经访问过的节点,然后用贪心算法遍历。
public int minJump(int[] jump) {
int[] visted = new int[jump.length];
//int[0]保存节点下表,int[1]保存访问的步数
Queue<int[]> q = new LinkedList<>();
//将节点0保存下来,同时将节点0记录
q.add(new int[]{0,0});
visted[0] = 1;
//当队列有值的时候进行遍历
while(!q.isEmpty()){
//队列出队,对当前节点进行处理
int[] cur = q.poll();
//向右跳跃
int rightIdx = jump[cur[0]]+cur[0];
if(rightIdx >= jump.length){
//向右跳跃可以达到数组最后一位或者超出数组表示已经找到最小值,可以跳出遍历
return cur[1]+1;
}else{
if(visted[rightIdx ] == 0){
//从来没有访问过,就入队,待下次处理
q.add(new int[]{rightIdx,cur[1]+1});
//增加访问标识
visted[rightIdx ] =1;
}
}
//向左跳跃
for(int i = 0 ;i<cur[0];i++){
if(visted[i] == 0){
//从来没有访问过,就入队,待下次处理
q.add(new int[]{i,cur[1]+1});
//增加访问标识
visted[i] =1;
}
}
}
return -1;
}
然后进行提交,超时白给。。。。主要的原因是因为当我们向左跳跃时,每次都是从0到cur-1的遍历,当cur的值越来越大的时候耗时会越来越多,此时是否可以考虑减少从左到cur-1的遍历次数,对于cur-1左边已经遍历过的值,我们再向左跳一次,然后再从左边那个值向右跳跃肯定不是最优解,所以我们需要引进maxLeft来保存左边访问的最大的值,在每个节点遍历完的时候将cur赋值给maxLeft
修改后的代码
public int minJump(int[] jump) {
int[] visted = new int[jump.length];
//int[0]保存节点下表,int[1]保存访问的步数
Queue<int[]> q = new LinkedList<>();
//将节点0保存下来,同时将节点0记录
q.add(new int[]{0,0});
visted[0] = 1;
int leftMax = 0;
//当队列有值的时候进行遍历
while(!q.isEmpty()){
//队列出队,对当前节点进行处理
int[] cur = q.poll();
//向右跳跃
int rightIdx = jump[cur[0]]+cur[0];
if(rightIdx >= jump.length){
//向右跳跃可以达到数组最后一位或者超出数组表示已经找到最小值,可以跳出遍历
return cur[1]+1;
}else{
if(visted[rightIdx ] == 0){
//从来没有访问过,就入队,待下次处理
q.add(new int[]{rightIdx,cur[1]+1});
//增加访问标识
visted[rightIdx ] =1;
}
}
//向左跳跃
for(int i = leftMax+1 ;i<cur[0];i++){
if(visted[i] == 0){
//从来没有访问过,就入队,待下次处理
q.add(new int[]{i,cur[1]+1});
//增加访问标识
visted[i] =1;
}
}
leftMax = cur[0];
}
return -1;
}
同时也可以采取动态规划,从后往前计算最小步数
public int minJump(int[] jump) {
int dp[] = new int[jump.length];
//给最后一个值赋值1
dp[jump.length-1] = 1;
for(int i = jump.lenght -2 ;i>=0 ;i--){
//如果在i的位置上,jump[i]能够直接跳到末尾那么只需要dp[i]就只需要一步,如果不能够的话,就只能在dp[jump[i]+i]的值上加1
dp[i] = jump[i]+i>=jump.length?1:dp[jump[i]+i]+1;
//因为修改了dp[i]的值,那么对于i+1到jump.length-1上的值,存在dp[j] >=dp[i]+1的情况的时候,可以通过跳到dp[i]然后达到末尾,需要的步数仅dp[i]+1,因此
for(int j = i+1 ;j<jump.length && dp[j]>=dp[i]+1;j++){
dp[j] = dp[i]+1;
}
}
return dp[0];
}
ps:题解都是源自于leetcode各位大佬的解答,我这种渣渣只要题够难,我就敢白给,希望下次再做的时候还能知道解法。