《剑指offer》面试题23:链表中环的入口节点

题目:如果一个链表中包含环,请找出该链表的环的入口结点。


思路一:

首先申请两个指针,快指针一次前进两步,慢指针一次前进一步,初始化都再链表头部。然后开始移动,如果他们指向了同一个节点,则证明有环,否则没环。当他们指向了同一个节点时,慢指针再次初始化,指向头结点。快慢指针前进步数都改为1,当他们再次指向同一个节点,这个节点就是环的入口节点。

讨论:单链表中的环的问题还有许多扩展,比如求环的长度,或者是如何解除环等等,可参见网上大神的这个总结贴关于单链表存在环的讨论

基于以上思路,java参考代码如下:

package chapter3;

import chapter2.ListNode;
import zuo_chapter2.P77_RemoveRepeat;

public class P139_MeetingNode {
    public static class ListNode{
        int val;
        ListNode next;
        public ListNode(int val){
            this.val=val;
            this.next=null;
        }
    }
    public static ListNode meetingNode1(ListNode head){
        if(head==null||head.next==null)
            return null;
        ListNode fast=head,slow=head;
        while(fast!=null&&fast.next!=null){
            slow=slow.next;
            fast=fast.next.next;
            //if(slow!=null&&slow==fast)//注意点1:不需要单独讨论slow,因为fast在slow前面,讨论fast是否为空即可
            if(slow==fast)  break;
        }
        if(fast==null||fast.next==null) return null;//注意点2:没有环,则直接返回null。
        slow=head;
        while(slow!=fast){
            slow=slow.next;
            fast=fast.next;
        }
        return slow;
    }
    //新增解法,自我感觉更简洁
    public static ListNode meetingNode(ListNode head){
        if(head==null) return null;
        ListNode fast=head,slow=head;
        do{
            fast=fast.next;
            if(fast==null)
                return null;
            fast=fast.next;
            slow=slow.next;
        }while (fast!=null&&fast.next!=null&&fast!=slow);//只能是fast==slow退出,空的情况前面if(fast==null)已经跳出。
        fast=head;
        while (fast!=slow){
            fast=fast.next;
            slow=slow.next;
        }
        return slow;
    }

    public static void main(String[] args){
        ListNode head = new ListNode(1);
        head.next= new ListNode(2);
        head.next.next = new ListNode(3);
        head.next.next.next = new ListNode(4);
        head.next.next.next.next = new ListNode(5);
        head.next.next.next.next.next = new ListNode(6);
        head.next.next.next.next.next.next =head.next.next ;
        System.out.println(meetingNode(head).val);
        System.out.println(meetingNode1(head).val);
    }
}


注意点见代码注解部分。

相关题目:求此环的长度。

设:链表头是X,环的第一个节点是Y,slow和fast第一次的交点是Z。各段的长度分别是a,b,c,如图所示。
在这里插入图片描述
第一次相遇时slow走过的距离:a+b,fast走过的距离:a+b+c+b。

因为fast的速度是slow的两倍,所以fast走的距离是slow的两倍,有 2(a+b) = a+b+c+b,可以得到a=c(这个结论很重要!)。

我们发现L=b+c=a+b,也就是说,从一开始到二者第一次相遇,循环的次数就等于环的长度。
java参考代码如下:

package chapter3;

public class P139_MeetingNodeOfLength {
    public static class ListNode{
        int val;
        ListNode next;
        public ListNode(int val){
            this.val=val;
            this.next=null;
        }
    }
    public static int meetingNodeoflength(ListNode head){
        if(head==null||head.next==null)
            return 0;
        ListNode fast=head,slow=head;
        int length=0;
        while(fast!=null&&fast.next!=null){
            length++;
            slow=slow.next;
            fast=fast.next.next;
            //if(slow!=null&&slow==fast)//注意点1:不需要单独讨论slow,因为fast在slow前面,讨论fast是否为空即可
            if(slow==fast)  break;
        }
        if(fast==null||fast.next==null) return 0;//注意点2:没有环,则直接返回null。
        return length;
    }

    public static void main(String[] args){
        ListNode head = new ListNode(1);
        head.next= new ListNode(2);
        head.next.next = new ListNode(3);
        head.next.next.next = new ListNode(4);
        head.next.next.next.next = new ListNode(5);
        head.next.next.next.next.next = new ListNode(6);
        head.next.next.next.next.next.next =head.next.next ;
        System.out.println(meetingNodeoflength(head));
    }
}

思路二:

对问题进行分步解决。
(1) 确定链表中是否包含环:双指针,一个每次移动一步,一个每次移动两步,如果两个指针最后相遇,那么就包含环。(注意,移动两步的指针要判断判断其第一步不为空,才能移动第二步)
(2) 确定环中点节点数目:在上面相遇的节点的基础上,移动一个指针,并计数,当指针回到该节点时,确定环中节点数目。
(3) 找到环的入口节点:从头开始,使用两个指针,第一个指针先移动n步(其中n为确定的环中的节点数目),第二个指针再开始同时移动,两个指针相遇的节点即为入口节点。

基于上述思路,C++参考代码如下:

/*
struct ListNode {
    int val;
    struct ListNode *next;
    ListNode(int x) :
        val(x), next(NULL) {
    }
};
*/ 
class Solution 
{ 
public: 
	ListNode* EntryNodeOfLoop(ListNode* pHead) 
	{ 
		ListNode* meetingNode = MeetingNode(pHead); 
		if(meetingNode == nullptr) 
			return nullptr; 
		
		//得到环中节点数目
		int nodesNumInLoop = 1; 
		ListNode* pNode1 = meetingNode; 
		while(pNode1->next != meetingNode)
		{ 
			pNode1 = pNode1->next; 
			++nodesNumInLoop; 
		} 

		//先移动pNode1,次数为环中节点数目
		pNode1 = pHead; 
		for(int i=0; i<nodesNumInLoop; ++i) 
			pNode1 = pNode1->next; 

		//再移动pNode1和pNode2
		ListNode* pNode2 = pHead; 
		while(pNode1 != pNode2)
		{ 
			pNode1 = pNode1->next; 
			pNode2 = pNode2->next; 
		} 

		return pNode1; 
	} 

	ListNode* MeetingNode(ListNode* pHead)
	{ 
		if(pHead == nullptr) 
			return nullptr; 

		ListNode* pSlow = pHead->next; 
		if(pSlow == nullptr) 
			return nullptr; 
		
		ListNode* pFast = pSlow->next; 
		while(pSlow != nullptr && pFast != nullptr)
		{ 
			if(pSlow == pFast) 
				return pFast; 
			
			pSlow = pSlow->next; 
			
			pFast = pFast->next; 
			if(pFast != nullptr) 
				pFast = pFast->next; 
		} 
		return nullptr; 
	} 
};
测试用例:

a.功能测试(链表中包含或者不包含环;链表中有多个或者只有一个节点)。
b.特殊输入测试(链表头节点为nullptr指针)。

参考:

https://blog.csdn.net/Koala_Tree/article/details/79020771
http://www.cnblogs.com/grandyang/p/4137302.html
http://www.cnblogs.com/hiddenfox/p/3408931.html

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值