Algorithm 面试谜题:跳跃游戏
跳跃游戏: 给定一个数组,从第一个元素开始,跳转到最后一个元素。跳转长度最多可以是数组中当前位置的值。最佳结果是当你以最少的跳跃次数达到目标时 找到最佳结果的算法是什么 例如:给定数组Algorithm 面试谜题:跳跃游戏,algorithm,pseudocode,Algorithm,Pseudocode,跳跃游戏: 给定一个数组,从第一个元素开始,跳转到最后一个元素。跳转长度最多可以是数组中当前位置的值。最佳结果是当你以最少的跳跃次数达到目标时 找到最佳结果的算法是什么 例如:给定数组A={2,3,1,1,4}到达末尾(索引列表)的可能方法如下 0,2,3,4(将2跳转到索引2,然后将1跳转到索引3,然后将1跳转到索引4) 0,1,4(将1跳到索引1,然后将3跳到索引4) 因为第二个解只有两次跳跃,所以它是最佳结果。从数组构造一个有向图。例如:i->j(如果| i-j | j作为图中的边)。现在
A={2,3,1,1,4}
到达末尾(索引列表)的可能方法如下
0,2,3,4
(将2跳转到索引2,然后将1跳转到索引3,然后将1跳转到索引4)0,1,4
(将1跳到索引1,然后将3跳到索引4)因为第二个解只有两次跳跃,所以它是最佳结果。从数组构造一个有向图。例如:i->j(如果| i-j | j作为图中的边)。现在,找到从第一个节点到最后一个节点的最短路径 FWIW,你可以使用Dijkstra的算法来寻找最短路径。复杂性是O(| E |+| V | log | V |)。由于| E |
a
和当前位置的索引i
,重复以下操作,直到到达最后一个元素
考虑a[i+1]
到a[a[i]+i]
中的所有候选“跳转到元素”。对于索引e
处的每个此类元素,计算v
=a[e]
+e
。如果其中一个元素是最后一个元素,请跳到最后一个元素。否则,跳转到具有最大v
的元素
i -> 0 1 2 3 4 5 6 7 8 9 10 11 12
a -> [4, 11, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
v -> 4 12 3 4 5 6 7 8 9 10 11 12 13
更简单地说,在触手可及的元素中,寻找在下一次跳跃中能让你走得最远的元素。我们知道这个选择,x
,是正确的,因为与您可以跳转到的所有其他元素相比,从y
可以访问的元素是从x
可以访问的元素的子集(向后跳转的元素除外,这显然是错误的选择)
该算法以O(n)为单位运行,因为每个元素只需要考虑一次(可以跳过第二次考虑的元素)
例子
考虑值的数组a
、标记、i
、索引和值的总和v
i -> 0 1 2 3 4 5 6 7 8 9 10 11 12
a -> [4, 11, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1]
v -> 4 12 3 4 5 6 7 8 9 10 11 12 13
从索引0开始,考虑接下来的4个元素。找到具有最大
v
的一个。该元素位于索引1处,因此跳到1。现在考虑下11个元素。目标触手可及,所以跳到目标上
演示
请参阅或。从左(端)开始…并遍历,直到编号与索引相同,使用此类编号的最大值。如果列表为
list: 2738|4|6927
index: 0123|4|5678
一旦你得到了这个数字,重复上面的步骤,直到你到达最右边
273846927
000001234
如果找不到与索引匹配的nething,请使用索引最远且值大于索引的数字。在本例中为7。(因为很快索引将大于该数字,您可能只需计算9个索引)动态规划
假设您有一个数组B
,其中B[i]
显示了在数组A
中达到索引i
所需的最小步数。你的答案当然在B[n]
中,因为A
有n
元素和索引从1开始。假设C[i]=j
表示您从索引j跳到索引i的路径(这是为了恢复以后采用的路径)
因此,算法如下所示:
set B[i] to infinity for all i
B[1] = 0; <-- zero steps to reach B[1]
for i = 1 to n-1 <-- Each step updates possible jumps from A[i]
for j = 1 to A[i] <-- Possible jump sizes are 1, 2, ..., A[i]
if i+j > n <-- Array boundary check
break
if B[i+j] > B[i]+1 <-- If this path to B[i+j] was shorter than previous
B[i+j] = B[i]+1 <-- Keep the shortest path value
C[i+j] = i <-- Keep the path itself
它可以通过一个简单的循环来恢复
该算法具有O(min(k,n)*n)
时间复杂度和O(n)
空间复杂度n
是A
中的元素数,k
是数组中的最大值
注
我保留了这个答案,但契肯的贪婪算法是正确的,而且效率更高。基本思想:
通过查找所有数组元素(所有i
,以便A[i]>=target-i
),开始构建从末尾到开头的路径
将每个这样的i
视为新目标,并(递归地)找到它的路径
选择找到的最小长度路径,附加目标
,返回
python中的简单示例:
ls1 = [2,3,1,1,4]
ls2 = [4,11,1,1,1,1,1,1,1,1,1,1,1]
# finds the shortest path in ls to the target index tgti
def find_path(ls,tgti):
# if the target is the first element in the array, return it's index.
if tgti<= 0:
return [0]
# for each 0 <= i < tgti, if it it possible to reach
# tgti from i (ls[i] <= >= tgti-i) then find the path to i
sub_paths = [find_path(ls,i) for i in range(tgti-1,-1,-1) if ls[i] >= tgti-i]
# find the minimum length path in sub_paths
min_res = sub_paths[0]
for p in sub_paths:
if len(p) < len(min_res):
min_res = p
# add current target to the chosen path
min_res.append(tgti)
return min_res
print find_path(ls1,len(ls1)-1)
print find_path(ls2,len(ls2)-1)
>>>[0, 1, 4]
>>>[0, 1, 12]
ls1=[2,3,1,1,4]
ls2=[4,11,1,1,1,1,1,1,1,1,1,1,1]
#在ls中查找到目标索引tgti的最短路径
def查找路径(ls、tgti):
#如果目标是数组中的第一个元素,则返回其索引。
如果tgti>>[0,1,4]
>>>[0, 1, 12]
我们可以计算远索引以跳转最大值,如果任何索引值大于远,我们将更新远索引值
简单O(n)时间复杂度解
public boolean canJump(int[]nums){
int-far=0;
for(int i=0;我不明白为什么要i-x[i]==j?@user973931如果可以一步从索引i移动到j,在图中用i->j作为一条边。你甚至不需要Djikstra。BFS很好,因为每条边都有一个恒定的权重。在这种情况下它是如何工作的:4,11,1,1,1,1,1,1,1,1,1?@ElKamina我用你的问题修改了我的答案。如果是3,5,1,4,1,1,1,1,1怎么办@Shahbaz,从0:3开始。跳转到1:5、2:1、3:4中v最大的元素,其中3:4具有最大值。3:4时,目标已达到,因此跳转到目标。这不是最优的。您过早地进行分支,无法保证以后在这条路径中不会有大的成本。请使用以下示例重试:2,6,1,15,1,1,1,1,1,1。注意6+1大于1+2。只有系统地搜索所有路径才能保证解决方案,而动态编程只是缓存重复的结果以更快地完成。您似乎已经仔细考虑了这一点,但它比我提供的解决方案更复杂。您是否看到我的解决方案中的缺陷?编辑:哦,我刚刚注意到您是o回答了我的答案,不是ElKamina。它实际上是一个非常简单的动态规划解决方案。它甚至不变成2D。另一方面,我做了很多算法设计
public boolean canJump(int[] nums) {
int far = 0;
for(int i = 0; i<nums.length; i++){
if(i <= far){
far = Math.max(far, i+nums[i]);
}
else{
return false;
}
}
return true;
}