题目地址:
https://leetcode.com/problems/jump-game-ii/
给定一个数组,每个数代表当前位置可以向后跳跃的最大步数,允许跳小于这个数字的步数。问从下标 0 0 0出发最少需要多少步可以跳到数组末尾。
基本思路是BFS。我们可以这样想,先将整个数组看成一个有向图的结构,先由
0
0
0出发,
a
[
0
]
a[0]
a[0]就代表了
1
1
1步所能到达的范围,将这个范围的所有的点看成
0
0
0位置的邻居节点,接着,遍历数组的从
1
1
1到
a
[
0
]
a[0]
a[0]之间的数字(也就是
0
0
0的所有邻居),对每个数字都去计算它的”最远邻居“,那么显然从
1
1
1步能到达的范围之后一直到这个最远邻居,就是至少两步所能到达的区域,然后以此类推。跳跃次数需要每次开始遍历”下一层邻居“的时候累加一次,直到某次能跳到结尾,就立刻返回即可。
具体例子如下:对于
(
2
,
3
,
1
,
1
,
4
)
(2,3,1,1,4)
(2,3,1,1,4)首先,
2
2
2是
0
0
0次跳跃所能到达的位置,就把第
0
0
0个数字节点看成图的出发节点,或者看成起点节点的”第
0
0
0层邻居“,我们标记一下:
(
2
(
0
)
,
3
,
1
,
1
,
4
)
(2(0),3,1,1,4)
(2(0),3,1,1,4)其次
a
[
0
]
=
2
a[0]=2
a[0]=2,所以至少跳一次所能到达的位置我们也标记一下:
(
2
(
0
)
,
3
(
1
)
,
1
(
1
)
,
1
,
4
)
(2(0),3(1),1(1),1,4)
(2(0),3(1),1(1),1,4)标记
1
1
1的节点就是”第
1
1
1层邻居“;接着遍历跳一次能到达的位置,我们就得到了跳两次能到达的位置:
(
2
(
0
)
,
3
(
1
)
,
1
(
1
)
,
1
(
2
)
,
4
(
2
)
)
(2(0),3(1),1(1),1(2),4(2))
(2(0),3(1),1(1),1(2),4(2))我们发现在更新跳两次能跳到的位置的时候,数组终点成为了标记
1
1
1的数字中某个数字的邻居,这时候就可以退出了,返回
2
2
2。
代码如下:
public class Solution {
public int jump(int[] nums) {
// 判掉不需要跳的情况
if (nums.length <= 1) {
return 0;
}
int res = 0;
// 初始化BFS时的队列,其只有一个元素0。[l, r]实际上就是队列里的元素
int l = 0, r = 0, far = 0;
// 当队列不空,则BFS
while (l <= r) {
res++;
// 扩展当前层,如果能走到数组末尾则返回步数
for (int i = l; i <= r; i++) {
far = Math.max(far, i + nums[i]);
if (far >= nums.length - 1) {
return res;
}
}
// 更新队列
l = r + 1;
r = far;
}
return res;
}
}
时间复杂度 O ( n ) O(n) O(n),空间 O ( 1 ) O(1) O(1)。
算法正确性是显然的,实际上就是求无权图的最短路长度,由BFS定义即可得到。
注:这题还有个动态规划解法,但复杂度较高,这里就省略了。