本篇博客给出的解法都是在O(1)的空间复杂度内解决问题,O(n)则可直接使用字典将遍历到的链表的结点存储下来,问题迎刃而解。
判断是否有环 141. 环形链表
例如上图这样的一个链表,图中的链表是存在环的,一种解决方法就是使用快慢指针,使用两个指针,均从head出发,慢指针每次走一步,快指针每次走两步,如果链表中存在环则在快慢指针一定会在某个时刻相遇。
为什么一定会相遇?当两个指针都进入环后,快指针每次都比慢指针多走一步,一开始两个指针间存在距离a,当之后多走的步加上a刚好等于环的长度时则两个指针就一定会相遇。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
bool hasCycle(ListNode *head) {
if(head == NULL || head->next == NULL)
return false;
ListNode* fast, *slow;
fast = slow = head;
while(fast != NULL && slow != NULL){
slow = slow->next;
fast = fast->next;
if(fast != NULL)
fast = fast->next;
if(fast == slow)
return true;
}
return false;
}
};
找到环的入口 142. 环形链表 II
解题思路稍微有点复杂,首先将上面的判断是否有环的函数的返回值修改为返回环中的某个节点的指针,得到环中任意一个节点的指针后即可得到环中的节点的数量,得到在环中的节点的数量(设为n)后再使用两个指针p1,p2,让p2先走n步,然后p1,p2保持相同的速度走,最后相遇的节点就是环的入口,这样p2一直领先与p1 n步,当p1走到环的入口时p2肯定也刚好走到环的入口。
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode(int x) : val(x), next(NULL) {}
* };
*/
class Solution {
public:
ListNode* hasCycle(ListNode *head) {
// 有环则返回环中的任意一个节点的指针,否则返回空
if(head == NULL || head->next == NULL)
return NULL;
ListNode* fast, *slow;
fast = slow = head;
while(fast != NULL && slow != NULL){
slow = slow->next;
fast = fast->next;
if(fast != NULL)
fast = fast->next;
if(fast == slow)
return slow;
}
return NULL;
}
int cycleLen(ListNode* head){
// 有环则返回环的节点的个数,否则返回0
ListNode* p = hasCycle(head);
if(p == NULL)
return 0;
ListNode* copyP = p;
p = p->next;
int cnt = 1;
while(p != copyP){
++cnt;
p = p->next;
}
return cnt;
}
ListNode *detectCycle(ListNode *head) {
if(head == NULL || head->next == NULL)
return NULL;
int cycleNodeNum = cycleLen(head);
if(cycleNodeNum == 0)
return NULL;
ListNode* p1, *p2;
p1 = p2 = head;
while(cycleNodeNum > 0){
p1 = p1->next;
cycleNodeNum--;
}
while(p1 != p2){
p1 = p1->next;
p2 = p2->next;
}
return p1;
}
};