原题:
Given a linked list, determine if it has a cycle in it.
Follow up:
Can you solve it without using extra space?
解题思路:
网上有个说法是,每扫描到一个点,让该节点的next指向头结点。这个想法很巧妙,只可惜会破坏链表本身的结构,而且这个是有问题的。因为你不确定这个环究竟是在头结点出现还是在后面的普通节点中出现。
所以,我用的方法是基于以上做法的改进:我们每经历一个点,就一直往后扫,看是否有任何的点和它产生回路。当扫到最后以后,回到第二个点,继续按照第一个的方法,扫描接下来所有的点是否和该点有回路。以此类推,最后我们会把所有的点之间的关系检查一边。quite cool。
产生回路应该有两种情况:1. 自身的点指向自身。2. 后面的点指向了前面的点。
代码:
class Solution {
public:
bool hasCycle(ListNode *head) {
if (head==NULL||head->next==NULL) return false;
ListNode *q = head;
while (q!=NULL){
ListNode *p = q->next;
while(p!=NULL){
if(p->next==q) return true;
p = p->next;
}
q = q->next;
}
return false;
}
};
这个代码一直报错runtime error。下面的改动考虑了节点指向自己的情况。
/**
* 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 *q = head;
while (q!=NULL){
ListNode *p = q->next;
while(p!=NULL){
//here is the change!
if(p==q) return true;
p = p->next;
}
q = q->next;
}
return false;
}
};
如上图所示,我没搞懂为什么会这样…If u know, plz do tell me!!!!!!
我这个算法的思想源自于以下代码的算法的思想:
class Solution {
public:
bool find(ListNode *head, ListNode *testpNode)
{
ListNode *p = head;
while (p != testpNode->next)
{
if(p == testpNode)
return false;
p = p->next;
}
return true;
}
bool hasCycle(ListNode *head) {
// IMPORTANT: Please reset any member data you declared, as
// the same Solution instance will be reused for each test case.
if(head == NULL)
return false;
ListNode *cur = head;
while(cur != NULL)
{
if(find(head, cur))
return true;
cur = cur->next;
}
return false;
}
};
这个程序AC了,但是速度很慢哦。
然后又看到了一个超级精简的方式,快慢指针法,算是方法2吧。
class Solution {
public:
bool hasCycle (ListNode *head){
if(head==NULL||head->next==NULL)return false;
ListNode *pFast,*pSlow;
pFast=head;
pSlow=head;
while(pFast&&pFast->next){
pSlow=pSlow->next;
pFast=pFast->next->next;
if(pFast==pSlow)return true;
}
return false;
}
};
AC了,并且很快!
但我有个疑惑,它这个应该之只检测了两个相邻的节点有没有环路吧?感觉没太理解。求指导求讲解!
==============================================================================================================================
关于这题我主要是有一些理解上的错误。
单向链表:每个链表节点都有一个next指针,通过名字知道,next存放的是下一个节点的位置,从而串起来的数据结构。
双向链表:每个链表节点除了next指针外还有prev指针。哪个节点next指针指向我,我的prev就指向那个节点。
根据本题所给的链表结构,明显是单项链表。
单向链表本身的特性便是只指向下一个。所以next只有一个,如果next可以指向多个便成为了一颗树!
所以本题当中,链表本身只可能存在一个回环。
所以算法二就很好理解了。一旦一个链表,存在了回路,就像是操场上跑步一样,跑在前面的人总会最后追到跑的慢的那个人。所以这个地方你的快慢指针分别取为next和nextnext还是n个next和n+1个next,性质是一样的【效率是否一样我还没有尝试过,大家可以自己试试】。
所以这个快慢指针的算法,真的是相当精彩阿~~~~
===============================================================================================================================
受同学指点,我自己写的那一个算法会超时是因为我并没有判断如果链表有环的话什么时候停下来,所以会陷入无限循环从而失败QAQ。仔细看看代码,貌似是这样的噢……