今天被问了单链表中环的问题,答得一团糟,所以复习还是很重要的
1.是否存在环
如果单链表中存在环的话,遍历链表会陷入死循环,一般会使用双指针的方法来进行判断。
思路:
使用双指针对链表进行遍历,快指针fast
和慢指针slow
,fast
每次走两步,slow
每次走一步
- 如果在遍历过程中,
fast
遍历到了null
,说明没有环- 如果
fast
和slow
指向同一个节点,说明存在环
while (fast.next != null && fast.next.next != null) {
fast = fast.next.next;
slow = slow.next;
if (fast == slow) {
return true;
}
}
return false;
2.环的长度
思路1:
记录下相遇节点存入临时变量tempPtr
,然后让slow
(或者fast
,都一样)继续向前走slow = slow -> next
;一直到slow == tempPtr
; 此时经过的步数就是环上节点的个数,也就是环的长度。
思路2:
从相遇点开始slow
和fast
继续按照原来的方式向前走slow = slow -> next
;fast = fast -> next -> next
;直到二者再次相遇,此时经过的步数就是环的长度 。
3.环的入口
思路:
假定链表头到环入口的距离是
len
,环入口到slow
和fast
交汇点的距离为x
,环的长度为R
。slow
和fast
第一次交汇时,设slow
走的长度为:d = len + x
,而fast
走的长度为:2d = len + nR + x,(n >= 1)
,从而我们可以得知:2len + 2x = len + nR + x
,即len = nR - x,(n >= 1)
。
- 使用一个cur指针指向链表头节点,一个inter指针指向第一次的交汇点。
- cur指针和inter指针一起往后遍历。
- cur指针和inter指针相等时,cur和inter指针指向的就是环的入口节点。
inter指针在遍历过程中可能多次(n >= 1)经过环入口节点,但当cur指针第一次达到入口节点时,inter指针此时必然也指向入口节点。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode * detectCycle(ListNode * head) {
if (NULL == head) return NULL;
ListNode * fast = head;
ListNode * slow = head;
while (1)
{
fast = fast->next ? fast->next : NULL;
if (NULL == fast) break;
fast = fast->next ? fast->next : NULL;
if (NULL == fast) break;
slow = slow->next;
if (slow == fast) break;
}
if (NULL == fast) return NULL;
ListNode * cur = head;
ListNode * inter = slow;
while (cur != inter)
{
cur = cur->next;
inter = inter->next;
}
return cur;
}
};