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;
    }