青蛙过河
难度:
标签:
题目描述
一只青蛙想要过河。 假定河流被等分为若干个单元格,并且在每一个单元格内都有可能放有一块石子(也有可能没有)。 青蛙可以跳上石子,但是不可以跳入水中。
给你石子的位置列表 stones
(用单元格序号 升序 表示), 请判定青蛙能否成功过河(即能否在最后一步跳至最后一块石子上)。开始时, 青蛙默认已站在第一块石子上,并可以假定它第一步只能跳跃 1
个单位(即只能从单元格 1 跳至单元格 2 )。
如果青蛙上一步跳跃了 k
个单位,那么它接下来的跳跃距离只能选择为 k - 1
、k
或 k + 1
个单位。 另请注意,青蛙只能向前方(终点的方向)跳跃。
示例 1:
输入:stones = [0,1,3,5,6,8,12,17] 输出:true 解释:青蛙可以成功过河,按照如下方案跳跃:跳 1 个单位到第 2 块石子, 然后跳 2 个单位到第 3 块石子, 接着 跳 2 个单位到第 4 块石子, 然后跳 3 个单位到第 6 块石子, 跳 4 个单位到第 7 块石子, 最后,跳 5 个单位到第 8 个石子(即最后一块石子)。
示例 2:
输入:stones = [0,1,2,3,4,8,9,11] 输出:false 解释:这是因为第 5 和第 6 个石子之间的间距太大,没有可选的方案供青蛙跳跃过去。
提示:
2 <= stones.length <= 2000
0 <= stones[i] <= 231 - 1
stones[0] == 0
stones
按严格升序排列
代码结果
运行时间: 91 ms, 内存: 24.1 MB
/*
* 题目思路:
* 通过流式编程简化代码逻辑。我们依然使用Map来记录每个石子和它可到达的距离。
* 使用流操作来遍历和更新这些数据结构。
*/
import java.util.*;
import java.util.stream.Collectors;
public class FrogJumpStream {
public boolean canCross(int[] stones) {
Map<Integer, Set<Integer>> map = Arrays.stream(stones)
.boxed()
.collect(Collectors.toMap(s -> s, s -> new HashSet<>()));
map.get(stones[0]).add(1);
return Arrays.stream(stones).anyMatch(stone ->
map.get(stone).stream().anyMatch(step -> {
int reach = stone + step;
if (reach == stones[stones.length - 1]) {
return true;
}
if (map.containsKey(reach)) {
map.get(reach).add(step);
if (step - 1 > 0) map.get(reach).add(step - 1);
map.get(reach).add(step + 1);
}
return false;
})
);
}
}
解释
方法:
这个题解采用了记忆化搜索的方法。首先用哈希表记录每个石头的位置,然后从第二个石头开始递归搜索。递归函数 dfs(u, step) 表示当前位于第 u 个石头,上一步跳了 step 个单位。在递归过程中,如果当前已经到达最后一个石头,则返回 True;否则枚举下一步跳 step-1、step 或 step+1 个单位的情况,递归判断是否能够到达终点。为了避免重复计算,使用记忆化存储已经计算过的状态。最后判断第一步是否合法并调用 dfs 函数即可得到答案。
时间复杂度:
O(n^2)
空间复杂度:
O(n^2)
代码细节讲解
🦆
如何确保哈希表中的索引与数组索引对应关系是正确的,特别是在多次调用递归函数时?
▷🦆
在递归函数中,为什么选择从第二个石头开始递归搜索而不是从第一个石头开始?
▷🦆
记忆化搜索中,状态的存储方式是如何设计的,以及如何通过状态判断是否进行过相同的递归调用?
▷🦆
递归函数dfs中,如果当前步长加上调整值k结果为0,为什么直接跳过该情况,这种设计有什么潜在的问题或考虑?
▷