Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/331.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
访谈:删除链表中的循环-Java_Java_Data Structures_Linked List - Fatal编程技术网

访谈:删除链表中的循环-Java

访谈:删除链表中的循环-Java,java,data-structures,linked-list,Java,Data Structures,Linked List,我在面试中被问到这样一个问题:“如何检测链表中的循环?”我解决了这个问题,但面试官马上问我如何删除链表中的循环。我摸索着 所以任何关于如何解决这个问题的指针,可能是伪代码,或者是方法定义 我对Java很熟悉,所以我将这个问题标记在Java下 例如,这个链表有一个循环 0--->1---->2---->3---->4---->5---->6 ▲ | |

我在面试中被问到这样一个问题:“如何检测链表中的循环?”我解决了这个问题,但面试官马上问我如何删除链表中的循环。我摸索着

所以任何关于如何解决这个问题的指针,可能是伪代码,或者是方法定义

我对Java很熟悉,所以我将这个问题标记在Java下

例如,这个链表有一个循环

 0--->1---->2---->3---->4---->5---->6
                  ▲                 |
                  |                 ▼
                 11<—-22<—-12<—-9<—-8
0-->1-->2-->3-->4-->5-->6
▲                 |
|                 ▼

11这个问题有两个部分:

  • 检测列表中是否有循环
  • 确定循环的开始
  • 一旦知道循环的开始位置,就很容易识别列表中的最后一个元素,因为它是列表中循环开始之后的元素,最后指向循环的开始。然后,将该元素的下一个指针/引用设置为
    null
    ,以更正循环链接列表(不是循环链接列表,最后一个元素指向第一个元素-这将是循环列表的一个特定实例),这很简单

  • 因为它涉及到使用两个以不同速度移动的指针/参考,所以这是检测循环的一种方法。如果有一个循环,两个指针(例如
    p1
    p2
    )将在有限的步骤后指向同一个元素。有趣的是,可以证明,它们相遇的元素到循环开始的距离(继续以相同的前进方向遍历列表)与循环开始到列表的头部的距离相同。也就是说,如果列表的线性部分有
    k
    元素,那么两个指针将在长度
    m
    的循环内的一个点
    m-k
    处相遇,从循环的开始或
    k
    元素到循环的“结束”(当然,这是一个循环,所以它没有“结束”-它再次只是“开始”)。这给了我们一种找到循环起点的方法:

  • 一旦检测到循环,让
    p2
    保持指向上面步骤的循环终止的元素,但重置
    p1
    ,使其指向列表的开头。现在,将每个指针一次移动一个元素。由于
    p2
    在循环内部开始,它将继续循环。在
    k
    步数(等于循环起点到列表头部的距离)之后,
    p1
    p2
    将再次相遇。这将为您提供循环开始的参考

  • 现在很容易设置
    p1
    (或
    p2
    )以指向开始循环的元素并遍历循环,直到
    p1
    最终指向开始元素。此时,
    p1
    正在引用“最后一个”元素列表,它的下一个指针可以设置为
    null


  • 下面是一些快速而肮脏的Java代码,假设一个
    节点
    的链表,其中
    节点
    有一个
    下一个
    引用。这是可以优化的,但它应该给你一个基本的想法:

    Node slow, fast, start;
    fast = slow = head;
    
    //PART I - Detect if a loop exists
    while (true)
    {
        // fast will always fall off the end of the list if it is linear
        if (fast == null || fast.next == null)
        {
            // no loop
            return;
        }
        else if (fast == slow || fast.next == slow)
        {
            // detected a loop
            break;
        }
        else
        {
            fast = fast.next.next; // move 2 nodes at at time
            slow = slow.next; // move 1 node at a time
        }
    }
    
    //PART II - Identify the node that is the start of the loop
    fast = head; //reset one of the references to head of list
    
    //until both the references are one short of the common element which is the start of the loop
    while(fast.next != slow.next) 
    {
        fast = fast.next;
        slow = slow.next;
    }
    
    start = fast.next;
    
    //PART III - Eliminate the loop by setting the 'next' pointer 
    //of the last element to null
    fast = start;
    while(fast.next != start)
    {
        fast = fast.next;
    }
    
    fast.next = null; //break the loop
    

    可能有助于解释第二部分背后的原因:

    假设循环的长度为M, 其余部分的长度是多少 链表是L,让我们算出 循环中的位置是什么 t1/t2首次见面

    定义循环中的第一个节点是 位置0,按照我们创建的链接 位置1,2,…,直到M-1。( 当我们在循环中行走时,我们的电流 位置为(行走长度)M型, 对吗?)假设t1/t2在 位置p,则其行程时间为 相同,(L+k1*M+p)/v=(L+k2*M+p)/2v 对于一些k1 因此得出结论,如果t1从 p、 t2从头部开始,在头部移动 同样的速度,然后将被授予满足 在位置0处,该节点的第一个节点 周期QED

    更多参考资料:

    • 还引用了采访书中的解释

    如果允许使用标识哈希映射(如),那么这非常容易解决。不过,它确实占用了更多的空间

    遍历节点列表。对于遇到的每个节点,将其添加到标识映射中。如果该节点已存在于标识映射中,则该列表具有循环链接,并且该冲突之前的节点是已知的(在移动之前检查或保留“最后一个节点”)--只需根据需要设置“下一个”即可打破该循环

    遵循这个简单的方法应该是一个有趣的练习:代码留给读者作为练习


    快乐编码。

    解决方案1-由以下人员提供:

    此解决方案的解释直接来自于本书:

    如果我们移动两个指针,一个带 速度1和另一个速度2,它们 如果链接到 列表有一个循环。为什么?想两个 在轨道上行驶的汽车;更快的车 总是会通过慢一点的

    这里棘手的部分是找到起点 循环的一部分。打个比方,想象一下, 两个人在跑道上赛跑, 一个跑得比另一个快两倍 其他的。如果他们同时出发 地点,他们下次什么时候见面?他们 下一次会议将在会议开始时举行 下一圈

    现在,让我们假设快跑运动员在比赛中领先k米 一个n步的圈。他们下一次什么时候来 满足他们将在9米前相遇 下一圈的开始。(为什么?快 跑步者会得到k+2(n-k) 步骤,包括其开头,以及 跑得慢的人会得到n-k 两个步骤都将是 循环的开始)

    现在,回到问题,当快跑者(n2)和 慢跑者(n1)在我们的周围移动 循环链表,n2将有一个 当n1时,在循环中领先 进入。具体来说,它将有一个 k的开头,其中k是数字 节点数
    public static LinkedListNode findStartOfLoop(LinkedListNode head) {
        LinkedListNode n1 = head;
        LinkedListNode n2 = head; 
    
        // find meeting point using Tortoise and Hare algorithm
        // this is just Floyd's cycle detection algorithm
        while (n2.next != null) { 
            n1 = n1.next; 
            n2 = n2.next.next; 
            if (n1 == n2) { 
                break; 
            }
        }
    
        // Error check - there is no meeting point, and therefore no loop
        if (n2.next == null) {
            return null;
        }
    
        /* Move n1 to Head. Keep n2 at Meeting Point.  Each are k steps
        /* from the Loop Start. If they move at the same pace, they must
         * meet at Loop Start. */
        n1 = head; 
        while (n1 != n2) { 
            n1 = n1.next; 
            n2 = n2.next; 
        }
        // Now n2 points to the start of the loop.
        return n2;
    }
    
    public static LinkedListNode findHeadOfLoop(LinkedListNode head) {
    
        int indexer = 0;
        Map<LinkedListNode, Integer> map = new IdentityHashMap<LinkedListNode, Integer>();
        map.put(head, indexer);
        indexer++;
    
        // start walking along the list while putting each node in the HashMap
        // if we come to a node that is already in the list, 
        // then that node is the start of the cycle 
        LinkedListNode curr = head;
        while (curr != null) {
    
            if (map.containsKey(curr.next)) {
                curr = curr.next;
                break;
            }
            curr = curr.next;
            map.put(curr, indexer);
            indexer++;
        }
        return curr;
    }
    
     0--->1---->2---->3---->4---->5---->6
                      ▲                 |
                      |                 ▼
                     11<—-22<—-12<—-9<—-8  
    
     0-->D->1-->D->2-->D->3->D-->4->D-->5->D-->6
                         ▲                      |
                      /                         ▼
                     11<—D<-22<—D<-12<—D<-9<—D<--8 
    
    
    Node(11)->next->next == D
    Node(11)->next =null
    
    //Find a Loop in Linked List and remove link between node
    
        public void findLoopInList() {
                Node fastNode = head;
                Node slowNode = head;
                boolean isLoopExist = false;
                while (slowNode != null && fastNode != null && fastNode.next != null) {
                    fastNode = fastNode.next.next;
                    slowNode = slowNode.next;
                    if (slowNode == fastNode) {
                        System.out.print("\n Loop Found");
                        isLoopExist = true;
                        break;
                    }
                }
                if (isLoopExist) {
                    slowNode = head;
                    Node prevNode = null;
                    while (slowNode != fastNode) {
                        prevNode = fastNode;
                        fastNode = fastNode.next;
                        slowNode = slowNode.next;
                    }
                    System.out.print("Loop Found Node : " + slowNode.mData);
                    prevNode.next = null; //Remove the Loop
                }
            }
    
    void removeTheLoop(Node *root)
    {
        std :: unordered_set < struct Node * > s;
        if(root == NULL)
            return ;
    
        s.insert(root);
        int before_size = s.size();
    
        while(1)
        {
            if(root -> next == NULL)
                return;
            s.insert(root -> next);
            if(before_size == s.size())
            {
                root -> next = NULL;
                return;
            }
            before_size = s.size();
            root = root -> next;
        }
    }