每天一道LeetCode-----判断链表是否有环,如果有,找到环的入口位置
2018-01-30 13:57
513 查看
Linked List Cycle
原题链接Linked List Cycle判断一个链表是否有环,空间复杂度是O(1)
如果不考虑空间复杂度,可以使用一个map记录走过的节点,当遇到第一个在map中存在的节点时,就说明回到了出发点,即链表有环,同时也找到了环的入口。
不适用额外内存空间的技巧是使用快慢指针,即采用两个指针walker和runner,walker每次移动一步而runner每次移动两步。当walker和runner第一次相遇时,证明链表有环
以图片为例,假设环的长度为R,当慢指针walker走到环入口时快指针runner的位置如图,且二者之间的距离为S。在慢指针进入环后的t时间内,快指针从距离环入口S处走了2t个节点,相当于从环入口走了S+2t个节点。而此时慢指针从环入口走了t个节点。
假设快慢指针一定可以相遇,那么有S+2t−t=nR,即S+t=nR,如果对于任意的S,R,n,总可以找到一个t满足上式,那么就可以说明快慢指针一定可以相遇,满足假设(显然可以找到)
而实际上,由于S<R,所以在慢指针走过一圈之前就可以相遇
所以如果链表中有环,那么当慢指针进入到环时,在未来的某一时刻,快慢指针一定可以相遇,通过这个也就可以判断链表是否有环
代码如下
/** * 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) { auto walker = head; auto runner = head; while(runner && runner->next) { walker = walker->next; runner = runner->next->next; if(walker == runner) return true; } return false; } };
Linked List Cycle II
原题链接Linked List Cycle II如果链表有环,寻找环入口位置
以图片为例,假设环入口距离链表头的长度为L,快慢指针相遇的位置为cross,且该位置距离环入口的长度为S。考虑快慢指针移动的距离,慢指针走了L+S,快指针走了L+S+nR(这是假设相遇之前快指针已经绕环n圈)。由于快指针的速度是慢指针的两倍,相同时间下快指针走过的路程就是慢指针的两倍,所以有2(L+S)=L+S+nR,化简得L+S=nR
当n=1时,即快指针在相遇之前多走了一圈,即L+S=R,也就是L=R−S,观察图片,L表示从链表头到环入口的距离,而R−S表示从cross继续移动到环入口的距离,既然二者是相等的,那么如果采用两个指针,一个从表头出发,一个从cross出发,那么它们将同时到达环入口。即二者相等时便是环入口节点
当n>1时,上式为L=nR−S,L仍然表示从链表头到达环入口的距离,而nR−S可以看成从cross出发移动nR步后再倒退S步,从cross移动nR步后回到cross位置,倒退S步后是环入口,所以也是同时到达环入口。即二者相等时便是环入口节点
所以寻找环入口的方法就是采用两个指针,一个从表头出发,一个从相遇点出发,一次都只移动一步,当二者相等时便是环入口的位置
代码如下
/** * Definition for singly-linked list. * struct ListNode { * int val; * ListNode *next; * ListNode(int x) : val(x), next(NULL) {} * }; */ class Solution { public: ListNode *detectCycle(ListNode *head) { auto walker = head; auto runner = head; while(runner && runner->next) { walker = walker->next; runner = runner->next->next; if(walker == runner) break; } if(!runner || !runner->next) return nullptr; auto headWalker = head; auto crossWalker = walker; while(headWalker != crossWalker) { headWalker = headWalker->next; crossWalker = crossWalker->next; } return headWalker; } };
其它的和环有关的题目记得还有
求环的长度
第一种方法是利用上面求出的环入口,再走一圈就可以求出长度,代码如下int cycleLen(ListNode* head) { auto cycleIn = detectCycle(head); int len = 1; auto walker = cycleIn; while(walker->next != cycleIn) { ++len; walker = walker->next; } return len; }
第二种方法是当快慢指针相遇时,继续移动直到第二次相遇,此时快指针移动的距离正好比慢指针多一圈,代码如下
int cycleLen(ListNode* head) { auto walker = head; auto runner = head; while(runner && runner->next) { walker = walker->next; runner = runner->next; if(walker == runner) break; } int len = 0; while(runner && runner->next) { ++len; walker = walker->next; runner = runner->next; if(walker == runner) break; } return len; }
相关文章推荐
- 判断单链表是否有环,如果有找出环的入口位置=>求两个相交链表的交点
- 题目:①判断一个单向链表是否有环,如果有环则找到环的入口节点。 ②判断两个单向链表是否相交,如果相交则找到交点节点。
- 如何判断一个链表是否有环? 2、如果链表为存在环,如果找到环的入口点?
- 每天一道LeetCode-----判断某棵树是否是二叉搜索树
- 每天一道LeetCode-----判断一个数是否是happy number(每一位的平方和最终为1)
- 判断链表是否带环,若带环,找到环的入口点
- leetcode 141 /142 判断链表是否有环&环入口
- 每天一道LeetCode-----存在一个由加油站组成的环路,判断是否可以从某个加油站出发环绕一周
- 判断链表是否有环 、 找到环的入口节点
- 每天一道LeetCode-----判断二叉树左右两边是否成镜像关系
- 判断一个链表中是否有环,并且得到环的入口位置
- 判断两链表是否相交,如果相交找到第一个交点
- 每天一道LeetCode-----判断两个二叉树是否相同
- 判断两个链表是否相交,如果相交如何找到第一个相交结点。
- 每天一道LeetCode-----在给定序列中找到满足nums[i]>nums[i-1]&&nums[i]>nums[i+1]的位置,要求时间复杂度是O(logN)
- 判断单链表中是否有环,如果有环则找到环的入口地址
- 用快慢指针判断单链表环,找到环入口 扩展到判断两个链表是否相交
- 如何判断一个链表是否有环,如果有环,并找出环的入口
- 每天一道LeetCode-----找到一个字符串在另一个字符串出现的位置,字符串内部顺序无要求
- 每天一道LeetCode-----判断给定字符串是否符合某个模式