一、什么是环形链表

你是否也见到过这样的链表,这种链表结构非常简单,只是让单链表的尾节点指向其中的某个节点,如果连续跟踪next指针,则会陷入循环当中。这就是环形链表。
二、如何判断一个链表中是否有环
1、哈希表
最容易想到的是方法是遍历所有的结点,每遍历一个节点,判断该节点此前是否被访问过。我们可以使用使用哈希表储存所有已经访问过的节点,每遍历到一个节点时,如果该节点已经存在于哈希表中,则说明该链表是环形链表,否则就将该节点加入哈希表中。重复这一过程,直到遍历完该链表为止。
2、快慢指针
还有一种方法是用快慢指针遍历链表,快指针一次走两个节点,慢指针一次走一个节点,如果两个指针可以相遇,则说明该链表是环形链表,如若快指针走到空,则该链表不是环形链表。
问题:(1)环形链表中为什么快慢指针可以相遇?
(2)快指针只能一次走两个节点吗?
先看第一个问题,我们先将快指针命名为fast,慢指针命名为slow,设slow在环内走过的节点为n,则fast在环内走过的节点为2n,环内的总结点数为c,当2n - n = c时,则两个指针相遇。第二个问题,若快指针一次走三个节点,则有3n - n = c,即c = 2n,也就是说环内的总结点数为偶数时两个指针才会相遇。以此类推fast一次走四个节点,c = 3n......
总之,当环内的总结点数等于两个指针走的节点数差值的倍数时,两个指针才会相遇,因此我们选用fast一次走两个节点。
三、找到环形的第一个节点
上面一个问题相对简单,而这个问题则需简单的证明,具体过程如下:
设fast走过的距离为l + x + n*c,slow走过的距离为l + x,则有l + x + n*c = 2*(l + x),即l = n*c - x = (n-1)*c + c - x,也就是说让另一个指针first从头走,slow从相遇点走,两个指针一次走一个节点,会在环形的第一个节点相遇。
具体的代码如下:
struct ListNode *detectCycle(struct ListNode *head) {
struct ListNode *fast,*slow;
fast = slow = head;
while(fast && fast->next)
{
fast = fast->next->next;
slow = slow->next;
if(slow == fast)
{
struct ListNode *first = head;
while(first != NULL)
{
if(first == slow)
{
return first;
}
first = first->next;
slow = slow->next;
}
}
}
return NULL;
}

868

被折叠的 条评论
为什么被折叠?



