快慢指针找链表入环的第一个节点-原理讲解及代码

废话不多说,今天来看看链表的一个难点,就是环的问题,看下面这个经典问题: 

给定一个单向链表,链表如果有环,返回入环的第一个节点(就是形成环形的第一个Node),如果无环返回null。

遇到这样一个题,有个巧妙的方法求解(不知道谁先想出来的,不过真的是天才),就是用快慢指针的解法,具体来说,就是定义两个Node,一快一慢,都从head出发,快的每次走2步,慢的每次走1步,如果相交了,那么相交的两个Node一个不动,另一个从head开始,大家再按相同的步调走(都是一次一步),第二次相交的那个Node就是形成环形的第一个Node!

其实,要是说为什么,我相信能想到这个方法的人不太可能是凭空想象出来,而是通过简单的数学化简,再结合算法来解的,所以嘛归根结底是个不难的数学问题,想清楚过程至关重要。

我们假设,从开始到入环节点距离为L,入环点和相遇点很可能不是一个点,所以我们设入环点到相遇点距离为d,快指针经过相遇点可能会绕这个环n圈,然后在相遇点和慢指针碰上,我们设一圈的周长为r,为了后面好理解,我们定义快指针绕圈的过程是从相遇点开始计算的,这样设定后,当快慢指针相遇时,我们就能表达出快慢指针各自走的路程了:

快:L+d+n*r

慢:L+d

然后的推导可以看下图:

由于快指针是一次2步,慢指针一次1步,相遇时快指针路程=慢指针路程*2。路程的等式简单的约分后,我们得到:L = nr - d

L就是从开始走到入环节点的距离,nr-d就是从相遇点开始绕n圈扣除d的距离,这个距离扣除d那就是走到了入环节点!所以等号左边设一个指针,右边也设一个指针,大家一起走这些距离,就必定相遇在入环节点了!

这样大家应该明白了吧,看下代码吧,代码其实很简单就两个循环,主要还是理解这个过程,不然很难长久记忆的!

package class06;

/**
 * @Description
 * @Package: class06
 * @Author: Ray
 * @CreateTime: 2020/8/5 0:18
 * @E-mail: 634302021@qq.com
 */
public class Test6 {
	
	//链表定义
	public static class Node {
		public int value;
		public Node next;
		
		public Node(int data) {
			this.value = data;
		}
	}
	
	// 找到链表第一个入环节点,如果无环,返回null
	public static Node getFirstLoopNode(Node head) {
		if (head == null || head.next == null || head.next.next == null) {
			return null;
		}
		// n1 慢  n2 快
		Node n1 = head.next; // n1 -> slow
		Node n2 = head.next.next; // n2 -> fast
		while (n1 != n2) {  //相交退出循环
			if (n2.next == null || n2.next == null ||  n2.next.next == null) {
				return null;
			}
			n2 = n2.next.next;
			n1 = n1.next;
		}
		n2 = head; // n2 从头开始走,n1 从相交点开始走
		while (n1 != n2) {  //相交的地方就是入环第一个节点,退出循环并返回
			n1 = n1.next;
			n2 = n2.next;
		}
		return n1;
	}
	
	public static void main(String[] args) {
		Node head1 = new Node(1);
		head1.next = new Node(2);
		Node node3 = new Node(3);   //定义一个入环节点
		head1.next.next = node3;    //入环节点
		head1.next.next.next = new Node(4);
		head1.next.next.next.next = new Node(5);
		head1.next.next.next.next.next = new Node(6);
		head1.next.next.next.next.next.next = new Node(7);
		head1.next.next.next.next.next.next.next = node3;   //入环节点
		System.out.println(getFirstLoopNode(head1).value);  //3
	}
}

 

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值