【图文解析】返回链表开始入环的第一个结点


例题描述

给定一个链表,返回链表开始入环的第一个结点。 如果链表无环,则返回 NULL

为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。

【说明:不允许修改给定的链表】


示例 1:
在这里插入图片描述

  • 输入:head = [3,2,0,-4], pos = 1
  • 输出:tail connects to node index 1
  • 解释:链表中有一个环,其尾部连接到第二个节点。

示例 2:
在这里插入图片描述

  • 输入:head = [1,2], pos = 0
  • 输出:tail connects to node index 0
  • 解释:链表中有一个环,其尾部连接到第一个节点。

示例 3:
在这里插入图片描述

  • 输入:head = [1], pos = -1
  • 输出:no cycle
  • 解释:链表中没有环。

解法一

哈希思想,使用STL提供的unordered_set进行节点地址去重,遍历链表,每次都在set中查询有没有添加过该节点(的地址),如果有,说明是入环点。如果遍历完都没有发现该入环点,即该链表无环,返回空。

代码实现

ListNode* EntryNodeOfLoop(ListNode* pHead) {
    while (pHead) {
        if (us.find(pHead) == us.end()) {
            us.insert(pHead);
            pHead = pHead->next;
        } else return pHead;
    }
    return nullptr;
}

此解法弊端在于需要额外创建空间存储节点,空间复杂度O(N)
那么下面这种解法即可优化至O(1)


解法二

使用快慢指针法,找到相遇点。另外创建2指针,一指针从入口出发,一指针从相遇点出发,两指针相遇点就是入口点。

这其实是一个数学问题:
假设此带环链表抽象成为一个线性结构。题设如下:

  • l从头结点到相遇点的距离
  • c从入环点到相遇点的距离
  • n为某个常数,仅代表圈数。(n >= 1)
    在这里插入图片描述
  1. 快指针直到相遇共走过的路程为:l+c+(n*r)。(n代表圈数,大小由步距决定)
    慢指针直到相遇共走过的路程为:l+c
  2. 慢指针入环之前,快指针进入环内空转等待,有可能转满一圈(环很大,结点很多)。也有可能会转很多圈(环小),影响不大。
  3. 相遇时,因为步距不同,说明走过的距离并不相等,而是二倍关系,即l+c+(n*r) =2(l+c),化简后为l=(n-1)*r+(r-c)
    (此时慢指针不可能跑满一整个环,那必定是只存在一个环周期的计算)
  4. (n-1)*r项可以忽略,因为在圈内转的圈数没有实际意义,重点还是在于相遇点。所以当一指针从相遇点开始,一指针再次从首结点开始,共同步进,步距相同时,一定会在入环点相遇,因为环外点要走r-c个单位才可以到达入环点,环内点不可能会路过入环点再走一圈,所以环内点走的距离也是r-c,二者刚好在入环点相遇。
    在这里插入图片描述

代码实现一

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

代码实现二

 ListNode *detectCycle2(ListNode *head) {
 	if (head == NULL) {
		return NULL;
	}
	struct ListNode *fast = head;
	struct ListNode *slow = head;

	while (1) {
		fast = fast->next;
		if (fast == NULL) {
			return NULL;	// return空代表无环,不存在的情况
		}
		fast = fast->next;
		if (fast == NULL) {
			return NULL;
		}
		slow = slow->next;
		if (fast == slow) {
			break;
		}
	}

	struct ListNode *n1 = head;
	struct ListNode *n2 = slow;

	while (n1 != n2) {
		n1 = n1->next;
		n2 = n2->next;
	}
	return n1;
 }
  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 8
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 8
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

giturtle

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值