【算法】链表中环的入口结点

题目描述

一个链表中包含环,请找出该链表的环的入口结点。
这个题目很经典:但是这种快慢指针的想法的原因我还没有明白其中的由来;还有不明白为什么快指针只能走2步,或者又是什么道理,没懂;
但是清楚的明白一点就是,快慢指针相遇的点一定是在环内,那么根据这个点在环内转圈再次遇到即就能得到环的大小n,也就是环中结点的个数;接着。让指针1先走环的n步.然后指针2从头结点开始走。则再次相遇即就是环的入口。
哇哦真的思维很巧妙,等我明白其中的道理来!
开始根据剑指offer书上写的算法如下:
package TestMyselfe;


public class Test56 {
	public static class ListNode {
        int value;
        ListNode next;
        public ListNode() {
        }
        public ListNode(int val) {
            this.value = val;
        }
        @Override
        public String toString() {
            return value + "";
        }
    }
	//找到快慢指针相遇的结点在环内
	 public static ListNode MeetingNode(ListNode pHead)
	    {
			if(pHead==null){
				return null;
			}
			ListNode slow=pHead;
			ListNode fast=pHead;
			while (fast!=null && slow!=null) {
				slow=slow.next;
				fast=fast.next.next;
				if(fast==slow){
					return fast;
				}
			}
			if(fast==null&&slow==null){
				return null;
			}
		 return fast;
	    }
	 /*
	  * 在有环的链表中找到环的入口结点的步骤:
	  * 1.指针p1和p2在初始化时都指向链表的头结点;
	  * 2.由于环中有4个结点,指针p1先在链表上向前移动4步;
	  * 3.指针p1和p2以相同的速度在链表上向前移动直到它们相遇。
	  * 它们相遇的结点就是环的入口结点;
	  */
	 public static ListNode EntryNodeOfLoop(ListNode pHead){
		 ListNode meetnode=MeetingNode(pHead);//环中相遇的结点是5
		 if(meetnode==null){
			 return null;
		 }
		 //得到结点中环的个数
		 int nodesinloop=1;
		 ListNode node1=meetnode;
		 //得到环中的结点个数
		 while(node1.next!=meetnode){
			 node1=node1.next;
			 ++nodesinloop;//统计环中结点的个数累加
		 }
		 //分别两个指针开始行走
		 node1=pHead;
		 //指针1先走
		 for (int i = 0; i < nodesinloop; i++) {
			node1=node1.next;//让node1先走nodesinloop步
		}
		 ListNode node2=pHead;
		 //指针1和指针2同时一步步走,相遇即就是环的入口
		 while(node1!=node2){
			 node1=node1.next;
			 node2=node2.next;
		 }
		return node1;
		 
	 }
	 public static void main(String[] args) {
		test01();
	}
	 // 1->2->3->4->5->6
	    private static void test01() {
	        ListNode n1 = new ListNode(1);
	        ListNode n2 = new ListNode(2);
	        ListNode n3 = new ListNode(3);
	        ListNode n4 = new ListNode(4);
	        ListNode n5 = new ListNode(5);
	        ListNode n6 = new ListNode(6);
	        n1.next = n2;
	        n2.next = n3;
	        n3.next = n4;
	        n4.next = n5;
	        n5.next = n6;
	        n6.next = n3;
	        System.out.println(EntryNodeOfLoop(n1));
	    }
	    // 1->2->3->4->5->6
	    //       ^        |
	    //       |        |
	    //       +--------+
}
后来发现还有更简单的,意思就一样,只不过就是不分开;先找到相遇点,接着一个从头走,一个继续走,一步一步,再次相遇即就是环的入口:
代码如下:
package TestMyselfe;


public class NewTest56 {
	public static class ListNode {
        int value;
        ListNode next;
        public ListNode() {
        }
        public ListNode(int val) {
            this.value = val;
        }
        @Override
        public String toString() {
            return value + "";
        }
    }
	/*
	 * 1->2->3->4->5->6->3
	 * 
	 */
	 public ListNode EntryNodeOfLoop(ListNode pHead){
		 if(pHead==null){
			 return null;
		 }
		ListNode slow=pHead;
		ListNode fast=pHead;
		//快慢指针在链表的找到相遇的点肯定在环内
		while(slow!=null&&fast.next!=null){
			slow=slow.next;
			fast=fast.next.next;
			if(fast==slow){
				break;
			}
		}
		//链表无环就是空
		if(slow==null||fast.next==null){
			return null;
		}
		ListNode node1=pHead;
		//环内相遇点开始
		ListNode node2=slow;
		//一步步再次相遇即就是环的入口
		while(node1!=node2){
			node1=node1.next;
			node2=node2.next;
		}
		 return node1;
	 }
}



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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值