您的位置:首页 > 其它

leetcode_Linked List Cycle

2014-01-23 14:01 302 查看


链表上找环,并且需要找出链表环的起始位置,如果无环,则输出空。
找环有几个思路,
1、遍历一遍链表,用hash记录遍历的节点指针,如果找到重复的,则有环。空间O(n),时间O(n)

2、每次遍历一个节点,则翻转指针,如果有环,那么最后遍历将回到头节点,如果没有环那么反转指针后,仍然回到尾部极点,遍历完毕
摘自http://blog.csdn.net/fty8788/article/details/6531280
如果要求不改变链表结构,则可以再遍历反转一次
这个方法的最大缺点是在多线程情况下不安全,
当多个线程都在读这个链表的时候, 检查环的线程会改变链表的状态, 虽然最后我们恢复了链表本身的结构, 但是不能保证其他线程能得到正确的结果

3、用快慢指针,类似于两个速度不同的人,指针p每次后移动两位,指针q每次后移动1位,如果有环,则快的一定会追上慢的,如果没环,则快的先到达了尾部,证明的话,想想也不难
假设T时刻慢指针在p点,快指针在q点 ,从T时刻开始,走i步,设圈长为m,如果(p+i)%m=( q+2*i)%m,则相遇
这个式子,很容易找到解
(p+i)%m=(q+2*i)%m
( q+2*i - p-i) %m= 0
( q-p+i)%m =0
显然 取i = p-q +km 都可以满足条件
bool hasCycle(ListNode *head) {
ListNode *p,*q;
if (head==NULL ) return false;//空链
if (head->next==NULL) return false ; //单个节点
p = head;q = head;
while(q!=NULL && q->next!=NULL)
{
p = p->next;
q = q->next->next;
if (p==q ) return true;
}
return false;
}


找入口点的思路
下面这篇文字写得很好http://www.cnblogs.com/wuyuegb2312/p/3183214.html
总的思路就是相遇后,会满足一个条件,从相遇位置每次走一步,恰好走非环部分长度,则走到了入口,这样我们在设计算法的时候,只要将其中一个指针又指向头,两者每次都移动一步,那么就可以到达入口。

证明:
设链表入口距离链表头长度为L,则p刚进入圈上时,设q已经在位置s上(这个s是相对于入口的距离),那么有
s=(2L-L)%m = L%m
接下来假设再走i步后,两指针相遇,则相遇条件为

i%m = (2*i+s)%m

i%m = (2*i+L%m)%m
运用同余性质
i%m = (2*i+L)%m
(i+L)%m=0

上式告诉我们,如果从相遇位置再走一个L,那么刚好走一个圈,换句话说,如果我们把p再次移到表头,p,q以同样速度每次移动一步,那么它们将会相遇在圈的入口处。

ListNode *detectCycle(ListNode *head){
ListNode *p,*q;
if (head==NULL ) return NULL;
if (head->next==NULL) return NULL;
p = head;
q = head;
int flag =0;
while(q!=NULL && q->next!=NULL)
{
p = p->next;
q = q->next->next;
if (p==q ){flag=1; break;}
}
if (flag ==0) return NULL;
p=head;
while(p!=q && q!=NULL)
{
p = p->next;
q = q->next;
}
return p;
}

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: