在计算机科学中,一个链表可以被描述为一系列节点,每个节点包含数据和指向下一个节点的引用。当一个链表中的某个节点的引用指向之前的某个节点,就会形成一个回环。换句话说,链表的末尾节点连接到了链表中的某个前面节点,导致链表成环。
弗洛伊德龟兔算法(Floyd's Tortoise and Hare Algorithm)是一种用于检测链表是否存在回环的快速方法。它基于两个指针,一个称为“龟”(tortoise)指针,另一个称为“兔”(hare)指针。算法的核心思想是,如果链表中存在回环,那么龟兔指针最终会在某一点相遇。
具体算法步骤如下:
- 初始化龟兔指针,都指向链表的头节点。
- 在每一步迭代中,龟指针向前移动一步,兔指针向前移动两步。
- 如果链表存在回环,那么龟兔指针最终会在某一点相遇,此时即可判断链表中存在回环。
这是因为,当龟兔指针进入回环部分后,它们的相对速度就像是在一个环形跑道上,兔指针的速度是龟指针的两倍。因此,兔指针会逐渐靠近龟指针,最终相遇在某个节点上。
弗洛伊德龟兔算法的时间复杂度为O(n),其中n是链表的节点数。由于算法仅使用了常数级的额外空间,因此它在判断链表中是否存在回环时非常高效。
在下一部分,我们将深入探讨如何在Java中实现弗洛伊德龟兔算法。
@Test
@DisplayName("弗洛伊德龟兔算法")
public void test30()
{
ListNode node8 = new ListNode(8,null);
ListNode node7 = new ListNode(7,node8);
ListNode node6 = new ListNode(6,node7);
ListNode node5 = new ListNode(5,node6);
ListNode node4 = new ListNode(4,node5);
ListNode node3 = new ListNode(3,node4);
ListNode node2 = new ListNode(2,node3);
ListNode node1 = new ListNode(1,node2);
node8.setNext(node4);
Integer i = isCircular(node1);
if (i != null)
{
System.out.println(true);
System.out.println(i);
}
else
{
System.out.println(false);
}
}
/**
* 判断是否有环
* @param head 链表头节点
* @return 如果链表中有回环就返回环入口的值,如果没有就返回null
* */
public Integer isCircular(ListNode head)
{
Integer i = null;
ListNode r = head;
ListNode t = head;
while (r != null && r.getNext() !=null)
{
r = r.getNext().getNext();
t = t.getNext();
if (r == t)//如果相遇了,就表明有环入口
{
r = head;
if (r != t)//如果尾节点不指向头节点,换句话说头节点不是入口节点
{
while (r != t)//让兔子扮演乌龟,这次他们都一次走一步,直到相遇
{
r = r.getNext();
t = t.getNext();
}
}
i = t.getVal();//相遇位置就是环入口
return i;
}
}
return i;//不存在回环的情况下直接返回null
}