原文: https://howtodoinjava.com/puzzles/how-to-detect-infinite-loop-in-linkedlist-in-java-with-example/

    这是一个非常常见的面试问题。 询问您是否有一个只能在一个方向上移动的链表,并且如果该链表中有一个循环,您将如何检测到它?

    好吧,如果您不知道答案,那就不要灰心丧气。 我个人的观点是,这类问题无法评估候选人的逻辑思维,因为这样的问题具有非常具体的答案。 您或者知道,或者不知道。

    对于这个特定问题,面试官寻找的最佳答案是“弗洛伊德循环发现算法”。 该算法提出了一种解决方案,建议您一次仅具有两个指针,而不是仅一个遍历列表的指针。 两个指针都将从链接列表的第一个节点开始,并使用next属性遍历。

    不同之处在于它们在每个步骤中跳跃的节点数。 第一个节点每次跳到下一个节点,另一个节点一次跳两个节点。 第一个节点称为较慢的节点乌龟,第二个节点较快的节点被称为兔子

    检测`LinkedList`中的无限循环的示例 - 图1

    龟兔算法

    这种遍历可确保如果链接链接中存在循环,则两个节点肯定会在其遍历路径中的某处相遇。 它具有O(n)复杂度。

    让我们使用 Java 示例代码对此进行验证。

    我已经写了一个最少可能的单链表代码,仅用于演示此示例。

    1. package com.howtodoinjava.demo.core;
    2. public class SinglyLinkedList {
    3. private Node start;
    4. public void add(Integer i)
    5. {
    6. Node node = new Node(i);
    7. if(start == null)
    8. start = node;
    9. else
    10. {
    11. Node temp = start;
    12. while(temp.next != null)
    13. {
    14. temp = temp.next;
    15. }
    16. temp.next = node;
    17. }
    18. }
    19. public Node getStart()
    20. {
    21. return start;
    22. }
    23. static class Node
    24. {
    25. Node(Integer i)
    26. {
    27. this.value = i;
    28. }
    29. private Integer value;
    30. private Node next;
    31. public Integer getValue() {
    32. return value;
    33. }
    34. public void setValue(Integer value) {
    35. this.value = value;
    36. }
    37. public Node getNext() {
    38. return next;
    39. }
    40. public void setNext(Node next) {
    41. this.next = next;
    42. }
    43. }
    44. }

    现在,让我们先在链表上进行循环测试,然后再进行循环测试。

    1. package com.howtodoinjava.demo.core;
    2. public class FindLoopsInLinkedList
    3. {
    4. public static void main(String args[]) {
    5. FindLoopsInLinkedList finder = new FindLoopsInLinkedList();
    6. SinglyLinkedList sampleList = new SinglyLinkedList();
    7. // First Insert randomly ten elements in a linked list
    8. for (int i = 0; i < 10; i++) {
    9. sampleList.add(i);
    10. }
    11. System.out.println("Loop Existence : " + finder.doesLoopExist(sampleList));
    12. System.out.println("Loop Existence : " + finder.doesLoopExist(finder.createLoop(sampleList)));
    13. }
    14. public boolean doesLoopExist(SinglyLinkedList listToCheck) {
    15. SinglyLinkedList.Node tortoise = listToCheck.getStart();
    16. SinglyLinkedList.Node hare = listToCheck.getStart();
    17. try {
    18. while (true) {
    19. tortoise = tortoise.getNext();
    20. hare = hare.getNext().getNext();
    21. if (tortoise == hare) {
    22. return true;
    23. }
    24. }
    25. } catch (NullPointerException ne) {
    26. return false;
    27. }
    28. }
    29. private SinglyLinkedList createLoop(SinglyLinkedList sampleList) {
    30. sampleList.getStart().getNext().getNext().getNext().setNext(sampleList.getStart().getNext());
    31. return sampleList;
    32. }
    33. }

    在上面的程序中,我们创建了一个链接列表,并在该列表中插入了 10 个元素。 否,当我们检查行号中是否存在循环时。 15 这是错误的。

    但是,当在第 167 行时,我们在链接列表内部创建了一个循环,结果如愿。

    上面程序的输出是这样的:

    1. Loop Existence : false [Line 15]
    2. Loop Existence : true [Line 16]

    如您所见,只要我们在行号中插入循环。 16,我们的算法实现能够检测到它。

    学习愉快!