【问题标题】:Circular Array Loop, detection圆阵列环路,检测
【发布时间】:2017-03-30 03:16:49
【问题描述】:

我正在解决一个问题,并且已经花费了一些时间。 问题陈述: 你得到一个正整数和负整数的数组。如果索引处的数字 n 为正,则向前移动 n 步。相反,如果它是负数 (-n),则向后移动 n 步。假设数组的第一个元素在最后一个元素的后面,最后一个元素在第一个元素的后面。确定此数组中是否存在循环。一个循环在一个特定索引处开始和结束,该索引沿循环有超过 1 个元素。循环必须是“向前”或“向后”。

示例 1:给定数组 [2, -1, 1, 2, 2],有一个循环,从索引 0 -> 2 -> 3 -> 0 开始。

示例 2:给定数组 [-1, 2],没有循环。

注意:给定的数组保证不包含元素“0”。

你能做到 O(n) 时间复杂度和 O(1) 空间复杂度吗?

这是我正在进行的解决方案,但是,当没有检测到循环时,我不确定应该如何结束 do-while 条件。如果没有检测到循环,我相信我的代码将无限运行。

public static boolean circularArrayLoop(int[] nums) {
    int size = nums.length;
    if(size < 2) return false;

    int loopStart = nums[0];
    int index = 0;
    int start = nums[0];
    do{
        if(nums[index] > 0){
            index = moveForward(index, nums[index], size);
        }else {
            index = moveBackward(index, Math.abs(nums[index]), size);
        }

    }while (loopStart != nums[index]);

}

【问题讨论】:

  • 如果数组中没有出现0,什么情况下可能没有循环? (如果方法只包含return true;,它是否算作 O(0)?
  • 您可以计算循环直到大小。当计数等于大小时停止循环
  • 我的理解:没有循环真的意味着“只有一个元素的循环”=>当​​一个元素指向自己时,这就是@Surace所说的。
  • “循环必须向前或向后”这是问题的一部分还是只是一个假设?

标签: java arrays loops


【解决方案1】:

这可以看作是有向(可能断开)图中的循环检测版本,或者更像是为给定图中的所有连接子图找到最小生成树。数组中的数字是顶点,根据顶点值在顶点之间形成一条边。没有已知的图解析算法可以在 O(1) 空间复杂度内解决它。这可以在 O(n) 时间复杂度中解决,因为最好的图形解析算法可以在 O(V+E) 时间内解决,并且在这种情况下 V=E,这使得在某些情况下可以使用 O(n) 时间复杂度来解决案例。最著名的算法是 Kruskal 的:http://www.geeksforgeeks.org/greedy-algorithms-set-2-kruskals-minimum-spanning-tree-mst/,它在 O(nlogn) 时间内求解。

【讨论】:

    【解决方案2】:

    由于保证没有值为 0 的元素,所以总会有一个循环。限定符是循环必须大于单个元素的长度。

    在这种情况下,当按照数组元素值的指示前进到下一个索引会导致到达相同的索引时,会出现“无”循环。

    快速和慢速移动光标可用于查找循环的开始。然后推进单个光标直到它返回到相同的索引将让您迭代循环的元素。如果单个前进将光标返回到相同的索引,则不存在循环。

    public static void main(String[] args) {
        int[] loop = {2, -1, 1, 2, 2};
        int[] noloop = {-1, 2};
        System.out.println(circularArrayLoop(loop));
        System.out.println(circularArrayLoop(noloop));
    }
    
    static int nextIndex(int[] nums, int cur) {
      // Get next index based on value taking into account wrapping around
    }
    
    static boolean circularArrayLoop(int[] nums) {
        int fast = 0;
        int slow = 0;
        do {
          // advance fast cursor twice
          // advance slow cursor once
        } while (fast != slow);
        int next = nextIndex(nums, fast);
        // return if the loop has more than a single element
    }
    

    【讨论】:

    • 如何使用两个变量?他们应该如何前进或后退?有两种选择,如果元素值为+ve,则索引向前移动,否则向后移动。在某个时候,索引将重置为 0 或 size-1。
    • stackoverflow.com/a/22264961/635411 这与如何计算下一个索引有关
    【解决方案3】:

    我认为不能保证循环会继续第一个元素,我错了吗?因此,你不能只做int loopStart = nums[0];

    • 如果您的示例 1 是 [2, -1, 1, 4, 2],那么循环将来自索引 0 -&gt; 2 -&gt; 3 -&gt; 2。而且,您对 loopstart 的检查不起作用,因为它检查了sums[0]

    一个好的解决方案是使用 2 个变量并以不同的速度(速度的两倍)移动它们。如果数组/链表是循环的,你会得到一个 var1 等于 var2 的点。

    这是伪代码:

    if array.length<=1
       return false
    
    int i=0;
    
    //loop is when "var1 == var2"
    //end is when "var1 == abs(array.length)"
    loop (until var1 == var2 or var1 reaches the end)
    
       var1 = moveToNext(var1)
       if (i++ % 2 == 0)
           var2 = moveToNext(var2)
    
    return var1 == var2;
    

    这与通常使用链表提出的问题非常相似:How to detect a loop in a linked list?

    【讨论】:

    • 我认为这并不能完全解决问题,因为循环需要大于单个元素。 Example 2: Given the array [-1, 2], there is no loop. 也没有“结束”,因为据说数组是循环的:var1 reaches the end
    • 这是我对此的看法:“......在一个特定的索引处结束,循环中的元素超过 1 个”。所以结束是当前索引指向自身时,这是我的理解。所以用 [-1, 2],当 var1 到达索引 [1] 时,它的下一个元素就是它自己,所以“到达终点”。你说得对,“1元素数组”确实是一种特殊情况,需要在进入逻辑之前处理
    猜你喜欢
    • 2015-09-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-04-17
    • 1970-01-01
    • 1970-01-01
    • 2013-07-28
    • 2013-05-02
    相关资源
    最近更新 更多