“开启掘金成长之旅!这是我参与「掘金日新计划 · 2 月更文挑战」的第 6 天,点击查看活动详情”
描述
给一个长度为n链表,若其中包含环,请找出该链表的环的入口结点,否则,返回null。
数据范围: n≤10000,1<=结点值<=10000
要求:空间复杂度 O(1),时间复杂度 O(n)
例如,输入{1,2},{3,4,5}时,对应的环形链表如下图所示:
可以看到环的入口结点的结点值为3,所以返回结点值为3的结点。
输入描述:
输入分为2段,第一段是入环前的链表部分,第二段是链表环的部分,后台会根据第二段是否为空将这两段组装成一个无环或者有环单链表
返回值描述:
返回链表的环的入口结点即可,我们后台程序会打印这个结点对应的结点值;若没有,则返回对应编程语言的空结点即可。
示例1
输入: {1,2},{3,4,5}
返回值:3
说明:返回环形链表入口结点,我们后台程序会打印该环形链表入口结点对应的结点值,即3
示例2
输入: {1},{}
返回值: "null"
说明:
没有环,返回对应编程语言的空结点,后台程序会打印"null"
示例3
输入: {},{2}
返回值: 2
说明:
环的部分只有一个结点,所以返回该环形链表入口结点,后台程序打印该结点对应的结点值,即2
知识点 :快慢双指针
双指针指的是在遍历对象的过程中,不是普通的使用单个指针进行访问,而是使用两个指针(特殊情况甚至可以多个),两个指针或是同方向访问两个链表、或是同方向访问一个链表(快慢指针)、或是相反方向扫描(对撞指针),从而达到我们需要的目的。
思路:
两个任务:
- 判断链表是否有环。
- 在有环的链表中找到环的入口。
对于第一个任务,可以参考判断链表中是否有环,主要思想是利用环没有末尾NULL,后半部分一定是环,然后快慢双指针相遇就代表有环。
具体做法:
- step 1:使用BM6.判断链表中是否有环中的方法判断链表是否有环,并找到相遇的节点。
- step 2:慢指针继续在相遇节点,快指针回到链表头,两个指针同步逐个元素逐个元素开始遍历链表。
- step 3:再次相遇的地方就是环的入口。
图示:
c++实现代码:
class Solution {
public:
//判断有没有环,返回相遇的地方
ListNode* hasCycle(ListNode *head) {
//先判断链表为空的情况
if(head == NULL)
return NULL;
//快慢双指针
ListNode* fast = head;
ListNode* slow = head;
//如果没环快指针会先到链表尾
while(fast != NULL && fast->next != NULL){
//快指针移动两步
fast = fast->next->next;
//慢指针移动一步
slow = slow->next;
//相遇则有环
if(fast == slow)
//返回相遇的地方
return slow;
}
//到末尾则没有环
return NULL;
}
ListNode* EntryNodeOfLoop(ListNode* pHead) {
ListNode* slow = hasCycle(pHead);
//没有环
if(slow == NULL)
return NULL;
//快指针回到表头
ListNode* fast = pHead;
//再次相遇即是环入口
while(fast != slow){
fast = fast->next;
slow = slow->next;
}
return slow;
}
};
js代码实现
/*function ListNode(x){
this.val = x;
this.next = null;
}*/
function EntryNodeOfLoop(pHead)
{
while(pHead !== null){
if(pHead.flag){
return pHead
}else{
pHead.flag = true;
pHead = pHead.next
}
}
return null
}
module.exports = {
EntryNodeOfLoop : EntryNodeOfLoop
};