这题非常经典,其实没见过的话短时间内想出空间复杂度O(1)的解法我觉得不容易。
解法1:用set或map。但要是要求空间复杂度O(1)就不行了。
ListNode *detectCycle(ListNode *head) {
if (!head) return NULL;
set<ListNode *> s;
ListNode* p = head;
while(p) {
if (s.count(p)) {
return p;
}
else {
s.insert(p);
p=p->next;
}
}
return NULL;
}
解法2:还是快慢指针法,我是参考网上的解法。
如图所示,p1和p2都从head出发,p1每次走1步,p2每次走2步,那么当p1和p2在环中某处相遇时,p2已经走s+m+kr, p1走了s+m+lr,也就是说相遇的时候p2和p1所花次数一样,但p2比p1多走了k-l圈。此处l>=0,k>=1, r为一圈周长。
另外我们知道p2走的路应该是p1的两倍,那么有s+m+kr=2(s+m+lr),可推出s+m=(k-2l)r。设n=k-2l,这个n圈实际上就是s+m的距离。
那么我们有s=nr-m。这个说明什么呢?说明如果让p1从head开始,p2从meet place开始,两个每次都只走一步,则p1走了s步,p2走了nr-m步,两者刚好相遇在环的起始点。
代码如下:
#include <iostream>
#include <set>
using namespace std;
struct ListNode {
int val;
ListNode *next;
ListNode(int x) : val(x), next(NULL) {}
};
ListNode *detectCycle(ListNode *head) {
if (!head) return NULL;
ListNode *p1=head, *p2=head;
while(p2) {
p1=p1->next;
p2=p2->next;
if (!p2 || !p2->next) return NULL;
p2=p2->next;
if (p2==p1)
break;
}
p1=head;
while(p1 && p2 && p1!=p2) {
p1=p1->next;
p2=p2->next;
}
return p1;
}
int main()
{
ListNode a=ListNode(1);
ListNode b=ListNode(2);
ListNode c=ListNode(3);
ListNode d=ListNode(4);
ListNode e=ListNode(5);
a.next=&b;
b.next=&c;
c.next=&d;
d.next=&e;
//e.next=&c;
ListNode* p = detectCycle(&a);
if (p)
cout<<p->val<<endl;
return 0;
}
我的另一个类似解法如下:
/**
* Definition of singly-linked-list:
* class ListNode {
* public:
* int val;
* ListNode *next;
* ListNode(int val) {
* this->val = val;
* this->next = NULL;
* }
* }
*/
class Solution {
public:
/**
* @param headA: the first list
* @param headB: the second list
* @return: a ListNode
*/
ListNode * getIntersectionNode(ListNode * headA, ListNode * headB) {
if (!headA || !headB) return NULL;
ListNode * nodeA = headA;
ListNode * endA = NULL;
while(nodeA && nodeA->next) {
nodeA = nodeA->next;
}
endA = nodeA;
endA->next = headA; // construct a circle
ListNode * node1 = headB;
ListNode * node2 = node1->next;
ListNode * meetNode = NULL;
while(node1 && node2 && node1->next && node2->next) {
if (node1 == node2) {
meetNode = node1;
break;
}
node1 = node1->next;
node2 = node2->next->next;
}
if (!meetNode) {
endA->next = NULL;
return NULL;
}
node1 = meetNode->next;
node2 = headB;
while(node1 != node2) {
node1 = node1->next;
node2 = node2->next;
}
endA->next = NULL;
return node1;
}
};
解法3:参考九章。
思路很清晰。先两个链表各自遍历,如果最后没走到一起就返回NULL。
然后算好两个链表的长度差,短的从头开始走,长的从长度差那个地方开始 走,走到一起的地方就是交汇点。
代码如下:
class Solution {
public:
/**
* @param headA: the first list
* @param headB: the second list
* @return: a ListNode
*/
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
// write your code here
if(headA == NULL || headB == NULL)
return NULL;
ListNode* iter1 = headA;
ListNode* iter2 = headB;
int len1 = 1;
while(iter1->next != NULL)
{
iter1 = iter1->next;
len1 ++;
}
int len2 = 1;
while(iter2->next != NULL)
{
iter2 = iter2->next;
len2 ++;
}
if(iter1 != iter2)
return NULL;
if(len1 > len2)
{
for(int i = 0; i < len1-len2; i ++)
headA = headA->next;
}
else if(len2 > len1)
{
for(int i = 0; i < len2-len1; i ++)
headB = headB->next;
}
while(headA != headB)
{
headA = headA->next;
headB = headB->next;
}
return headA;
}
};