关键知识
链表的处理
解题思路
一种简单的做法就是顺序遍历每个结点,然后每次都判断该点是否在前面遍历过的结点中,可以采用两层遍历的做法或者是采用set来存储前面的结点,然后每次在set中find这个结点。但是set会将空间复杂度大大增加。
另一种做法就是采用快慢两个指针在链表中遍历,如果快慢指针会相遇,则说明链表中存在环,否则不存在。存在环后,再通过一定的数学方法找到起始结点。该做法的时间复杂度为O(n),空间复杂度为O(1),为最佳做法。
简单做法
首先初始化一个set,然后遍历链表,不断地比较该节点是否出现过,出现过则说明存在环,没出现过就将此节点加进set,直到遍历完整个链表。
时间复杂度: 平均为O(n),最差为O(n^2)
空间复杂度: O(n)
最佳解法
采用两个指针分别为fast和slow,fast指针遍历的速度为slow的两倍,如果在fast到达链表结尾时两个指针不相遇,则说明链表不存在环,如果存在环,由于两者速度不一样,必定相遇,且相遇也说明了链表存在环。相遇之后,用meet指针记录相遇结点,然后用p指针指向起始结点head,然后两个指针同步遍历,直到两者相遇的结点即为环的起始结点。证明过程:
时间复杂度: O(n)
空间复杂度: O(1)
题目
简答做法
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
set<ListNode*> sl;
ListNode* p = head;
while (p) {
if (sl.find(p) == sl.end()) {
sl.insert(p);
p = p->next;
} else {
return p;
}
}
return nullptr;
}
};
最优解法
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode *fast = head, *slow = head, *meet = nullptr;
if (!head) return nullptr;
fast = head->next ? head->next->next : head->next;
slow = head->next;
while (fast) {
if (fast == slow) {
meet = slow;
break;
}
fast = fast->next ? fast->next->next : fast->next;
slow = slow->next;
}
if (!meet) return nullptr;
ListNode* p = head;
while (p != meet) {
meet = meet->next;
p = p->next;
}
return meet;
}
};