剑指Offer 面试题23:链表中环的入口节点 Java代码实现

题目描述

一个链表中包含环,请找出该链表的环的入口结点。

  这题又用到了一快一慢两个指针的方法,快指针一次走两步慢指针一次走一步。可以证明,若存在环路,则这两个指针一定会在环路中某个地方相遇。这也是检测一个链表是否存在环路的方式。

  接下来具体分析一下这两个指针什么时候会相遇。假设从第一个数据节点到环的入口节点需要走k步,那么当慢指针slow走到环的入口节点时,快指针fast已经走了2k步,其中在环内走了k步。k可能会比环路的节点数大会很多,快指针应该距离环的入口节点mod(k,LOOP_SIZE)步,记为K。
也就是说:当slow走到入口节点时,fast在环内走了K步。slow落后于fast,相距K。或者说fast落后slow,相距(LOOP_SIZE-K)步。
每走一次,fast都会靠近slow步。于是两者将在(LOOP_SIZE-K)步之后相遇,这个相遇点就距离环入口 K 步。

  下面说一下怎么找到环路起始处。上面说了,在相遇点,还需走K步到环的入口节点 。
因为K=mod(k,LOOP_SIZE), k=K+M*LOOP_SIZE,所以也可以说从碰撞处,需要走k步到达环的入口节点(就是在环里面多绕几圈)。也就是说,链表第一个数据节点和碰撞节点都需要走k步到达环的入口节点。碰撞后,我们把一个指针指向链表第一个节点,一个指针指向碰撞处,以同样的速度移动,他们会在环的入口处相遇。
实现的Java代码如下(这儿的链表包含一个没有数据域的头结点):
public static ListNode entryNodeOfLoop2(ListNode head){
		if(head==null||head.next==null)
			return null;
		
		ListNode slow=head.next,fast=head.next;
		
		//找到碰撞处 ,处于链表中LOOPSIZE-k步
		while(fast!=null&&fast.next!=null){
			slow=slow.next;
			fast=fast.next.next;
			if(fast==slow)
				break;
		}
		//错误检查 没有环路
		if(fast==null||fast.next==null){
			return null;
		}
		//发生碰撞后,再将slow指向链表第一个数据点,fast流在碰撞处
		//同速度走,相碰的地方就是 环路起始处
		slow=head.next;
		while(slow!=fast){
			slow=slow.next;
			fast=fast.next;
		}
		return fast;
	}
剑指Offer上的实现,有些许不同。上面的的分析比较全面,分析后代码也更简洁。Offer中的方法更好理解,但是代码量稍多一点。各有优点,大家自己斟酌。Offer中的实现思路如下:

1.通过一快一慢两个指针找到碰撞处

2.遍历环,得到环中的节点数N

3.两个指针都初始指向第一个节点,然后第一个指针先走N步(两个指针相距就是环内节点数),然后一起走,第一次相遇的点就是环的入口。
public static ListNode meetingNode(ListNode head){
		if(head==null||head.next==null)
			return null;
		
		ListNode slow=head.next,fast=head.next;
		
		while(fast!=null&&fast.next!=null){
			slow=slow.next;
			fast=fast.next.next;
			if(fast==slow)
				return fast;
		}
		return null;
	}
	
	public static ListNode entryNodeOfLoop(ListNode head){
		ListNode meetingNode=meetingNode(head);
		if(meetingNode==null) return null;
		
		//求环节点数目
		int loopCount=1;
		ListNode pNode=meetingNode;
		while(pNode.next!=meetingNode){
			pNode=pNode.next;
			loopCount++;
		}
		//从第一个数据节点开始移动loopCount次
		pNode=head.next;
		for(int i=0;i<loopCount;i++){
			pNode=pNode.next;
		}
		//同时移动
		ListNode pNode2=head.next;
		while(pNode!=pNode2){
			pNode=pNode.next;
			pNode2=pNode2.next;
		}
		
		return pNode;
	}


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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值