在链表复杂性中查找循环?

时间:2015-11-26 11:42:27

标签: data-structures linked-list

在链表中找到循环的正常方案是移动指针一次并移动其他指针两次。如果它们相遇,则链表中有一个循环。

但如果我将第二个指针移动三次或四次会发生什么。它会降低复杂性吗?为什么我们只需要移动第二个指针两次。

boolean hasLoop(Node first) {
    Node slow, fast; 
    slow = fast = first; 
    while(true) {
        slow = slow.next; 
        if(fast.next != null)
            fast = fast.next.next; 
        else
            return false;  

        if(slow == null || fast == null) 
            return false;

        if(slow == fast) 
            return true;
    }
}

而不是fast.next.next,为什么我没有fast.next.next.next或fast.next.next.next.next.next?

4 个答案:

答案 0 :(得分:3)

由于快速指针以双倍速度移动而不是慢速移动,因此两个指针之间的距离将始终增加1(最初它们之间的距离为2)。

现在假设循环存在,当慢速指针进入循环时,慢速和快速之间的距离称为“x”,循环的长度为“d”。所以现在下一次慢速和快速将移动,它们之间的距离将变为x + 1,然后在下一次移动时它将是x + 2,然后是x + 3,依此类推。只要它们之间的距离是d的倍数,快速和慢速就会相遇。因此,通过将它们之间的距离增加1,我们会检查每个值。

现在考虑快速移动三倍的情况,然后在它们之间的每一步距离将增加2,即x,x + 2,x + 4,依此类推。因此,这两个指针可能不会相互见面并相互交叉,如果发生这种情况,您的程序将永远不会终止。 如果快速指针的速度是四次,五次等,情况类似。

答案 1 :(得分:1)

当指针移动速度超过较慢指针速度的两倍时,它可以与第一个指针重叠而不会满足它。如果每次两个指针都经过(在同一个循环内)时会发生这种情况,那么算法将1.从不找到循环而且2.永远不会终止。

答案 2 :(得分:0)

复杂性不计入循环步数,而是访问的节点数。 因此,上述算法只会使NullPointerExceptions更加困难。循环也可以是任意长度的节点。

在朴素解决方案中将需要二次算法:检查节点[i]没有作为节点[j]出现所有j<岛

更快的算法使用内存。

通过生成标记(一种引用计数)作为节点的 field

// Complexity O(N)

class Node
    Node next;
    boolean generation;

class List
    Node first;
    boolean nodesGeneration; // All nodes false or true

    boolean isCircular() {
        for (Node node = first; node != null; node = node.next) {
            if (node.generation != nodesGeneration) {
                return false;
            }
            node.generation = !node.generation;
        }
        nodesGeneration = !nodesGeneration;
        return true;
    }

或者如果这不可能/期望:使用将Object引用作为键的IdentityHashMap。就像一组对象引用(==)。

// Complexity O(N), worse case O(N . log N)

    boolean isCircular() {
        Map<Node, Boolean> checkedNodes = new IdentityHashMap<>();
        for (Node node = first; node != null; node = node.next) {
            if (checkedNodes.containsKey(node)) {
                return false;
            }
            checkedNodes.put(node, Boolean.TRUE);
        }
        return true;
    }

答案 3 :(得分:0)

不仅如此。

如果循环在链接列表中,程序将更快地找到循环。

例如使用fast.next.next,while将在10000节点链接列表中完成9998次。

但如果最后一个节点指向节点800,则需要9198次才能找到循环