✨✨欢迎大家来到Celia的博客✨✨
🎉🎉创作不易,请点赞关注,多多支持哦🎉🎉
所属专栏:OJ题
目录
一、题目要求
- 给定一个链表的头节点
head
,返回链表开始入环的第一个节点。 如果链表无环,则返回null
。- 如果链表中有某个节点,可以通过连续跟踪
next
指针再次到达,则链表中存在环。 为了表示给定链表中的环,评测系统内部使用整数pos
来表示链表尾连接到链表中的位置(索引从 0 开始)。如果pos
是-1
,则在该链表中没有环。注意:pos
不作为参数进行传递,仅仅是为了标识链表的实际情况。- 不允许修改 链表。
二、解题思路
- 根据题意,我们需要先判断链表中是否有环存在,如果没有环,返回NULL。
- 如果链表有环,则可以使用快慢指针来解决这个问题:
- 先使用快慢指针遍历链表节点,并记录快慢指针相遇时的链表节点meet
(对于快慢指针相遇问题有疑惑请看:OJ-环形链表)- 定义两个指针slow,fast,分别指向链表头节点、meet
- 让这两个指针分别从其当前位置开始同时移动,当两个指针相遇的时候,就是该链表开始入环的节点。
三、代码实现
//判断是否有环 bool hasCycle(struct ListNode *head) { struct ListNode *low = head; struct ListNode *fast = head; while(fast && fast->next) { low = low->next; fast = fast->next->next; if(low == fast) return true; } return false; } //返回快慢指针相遇节点 struct ListNode *common(struct ListNode *head) { struct ListNode *low = head; struct ListNode *fast = head; while(fast && fast->next) { low = low->next; fast = fast->next->next; if(low == fast) break; } return low; } //返回入环节点 struct ListNode *detectCycle(struct ListNode *head) { bool flg = hasCycle(head); if(flg == false) return NULL; struct ListNode *com = common(head); struct ListNode *tmp = head; while(com != tmp) { com = com->next; tmp = tmp->next; } return com; }
四、关于解题思路理论的推导
我们把时间定位在快慢指针相遇时记录meet节点的时刻:
假设相遇时:
- 此时fast已经走过了完整的x圈(x >= 1),slow一次走一步,fast一次走两步
- 相遇时slow走过的路程:L + N
- 相遇时fast走过的路程:L + x * C + N
- 易知:L + x * C + N = 2 *(L + N)
- 化简:L = x * C - N
- 再次化简:L = (x - 1)* C + (C - N)
- 由此可推断:(x - 1)* C 为完整一圈,可看作一个周期
- 剩下:L = 周期 + (C - N)
故若有两个指针分别指向head和meet节点,当它们同步移动时它们一定会相遇,且相遇的节点一定是链表的入环节点。