剑指 Offer II 022. 链表中环的入口节点(快慢指针法)

链表中环的入口节点

题目描述:

给定一个链表,返回链表开始入环的第一个节点。 从链表的头节点开始沿着 next 指针进入环的第一个节点为环的入口节点。如果链表无环,则返回 null

(中等难度的题)

本题需要解决两个问题:

1.判断链表是否有环

2.找到入环的第一个节点

本题用快慢指针做的结果:

 判断是否有环

设定两个指针p,q,从头节点开始

p一次走一步,q一次走两步

当q到达NULL时,说明链表无环

当p、q相遇(快指针追上慢指针)则说明链表有环。

    struct ListNode*p = head->next;//慢指针
    struct ListNode*q = head->next->next;//快指针
    if(p==NULL||q==NULL)//生成成功
        return NULL;
    while(q!=NULL&&q->next!=NULL)//下面用了二级指针,所以q->next不能为空
    {
        if(p==q)
            break;
        p = p->next;
        q = q->next->next;
    }
    if(q==NULL||q->next==NULL)//如果快指针走到空了,说明链表没有环
        return NULL;

上面代码初始化指针时已经走了一步了,也可以把p、q都初始都指向head

找到第一个入环的节点

完成判断后,此时的p、q就处在环内的某一节点

下一步的结论是:设置r指向头节点,此时p和r每次各走一步,当p、r相遇时交点就为所求节点。

代码如下:

    struct ListNode* r = head;
    while(r!=p)
    {
        r = r->next;
        p = p->next;
    }
    return r;

下面来证明一下这个结论:

 p、q相遇,说明q多比p走x*c的距离,x为多走的整数圈数。

则:p走过的距离为m+n, q走过的距离为m+n+c*x

因为p一次走一步,q一次走两步

所以m+n+c*x = 2*(m+n)

m+n = c*x

m = c*x-n+c-c

m = c*(x-1)+c-n
去掉整数圈长度c*(x-1),可得m = c-n;

所以一个指针从头开始走,一个从相遇点开始走,最终会在入环第一个点相遇。

完整代码(C语言)

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     struct ListNode *next;
 * };
 */
struct ListNode *detectCycle(struct ListNode *head) {
    if(head==NULL||head->next==NULL)
        return NULL;
    struct ListNode*p = head->next;
    struct ListNode*q = head->next->next;
    if(p==NULL||q==NULL)
        return NULL;
    while(q!=NULL&&q->next!=NULL)
    {
        if(p==q)
            break;
        p = p->next;
        q = q->next->next;
    }
    if(q==NULL||q->next==NULL)
        return NULL;
    struct ListNode* r = head;
    while(r!=p)
    {
        r = r->next;
        p = p->next;
    }
    return r;
}

当然,这题也可以用穷举暴力解

判断是否有环

bool HaveRing(List plist)
{
    assert(plist != NULL);
	if (plist == NULL)
		return false;
	if (plist->next == plist)//循环空表
		return true;
	if (plist->next == NULL)//空表
		return false;
	Node* q = plist->next;
	while (q!=NULL)
	{
		Node* p = plist;
		while (p != q)
		{
			if (p->next == q->next)
				return true;
			p = p->next;
		}
    	if (p->next == q->next && p == plist)//环指向头
			return true;
		q = q->next;
	}
	return false;
}

穷举的话第一个入环节点也很好找了,但是这样时间复杂度太高,不建议使用。

  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

曦樂~

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

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

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

打赏作者

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

抵扣说明:

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

余额充值