题目描述
给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,返回null。
输入描述:
输入分为2段,第一段是入环前的链表部分,第二段是链表环的部分,后台将这2个会组装成一个有环或者无环单链表
返回值描述:
返回链表的环的入口结点即可。而我们后台程序会打印这个节点
示例1
输入: {1,2},{3,4,5} 返回值: 3 说明: 返回环形链表入口节点,我们后台会打印该环形链表入口节点,即3
示例2
输入: {1},{} 返回值: "null" 说明: 没有环,返回null,后台打印"null"
从题已知信息
- 链表无环输出null
方法一: 哈希法
思路和算法
最容易想到的方法是遍历所有节点,每次遍历到节点时,判断该节点此前是否被访问过。
全部代码如下:
import java.util.*; public class Solution { public ListNode EntryNodeOfLoop(ListNode pHead) { ListNode curr = pHead; // 用来记录访问过的路径 Set<ListNode> set = new HashSet<>(); while(curr!=null){ //如果有环,则相遇的第一个节点就是入口节点 if(set.contains(curr)){ return curr; }else{ //如果没有访问过就加入 set.add(curr); } curr = curr.next; } //无环输出null return null; } }
复杂度分析
时间复杂度:O(N) ,遍历一次 O(N) .
空间复杂度:O(N) ,hashset随链表长度增大
方法二:快慢指针
思路和算法
我们使用两个指针,fast 与 slow
如果链表中存在环,则 fast 指针 slow 指针在环中相遇。
如果不存在,fast将会先走到结尾 null
根据这个图片,假设在橘色点相遇,我们可以知道 fast指针走的路程为:
fast = a + n ( b + c ) + b // fast 不知道在环中跑多少圈,这里假定 n , b + c 是一圈的距离
根据题意可知道,2slow = fast, 如果有环的话,slow没跑完一圈必定被fast追上
slow = a + b
a + ( n + 1) b + nc = 2 ( a + b )
(n - 1 ) b + nc = a
nb + nc - b = a // 在补一个 + c - c 可以转换成下面的公式
c + (n - 1) ( b + c )= a
由此知道, 从相遇点开始跑 , fast 跑 c + (n - 1) ( b + c ) 跟 slow 跑 a 的距离是一样的
- 先找出相遇点
- 在相遇之后把 slow 归为 0 位置,从头开始跑,此时相遇的点就是入口节点
import java.util.*; public class Solution { public ListNode EntryNodeOfLoop(ListNode pHead) { ListNode fast = pHead, slow = pHead; while (fast != null && fast.next != null) { fast = fast.next.next; slow = slow.next; //如果快慢指针相遇了,就是图中的紫色的点 if (fast == slow) { // 慢指针从头在来,他们再次相遇时,就是入口 slow = pHead; while (slow != fast) { fast = fast.next; slow = slow.next; // 找到入口,跳出 if (fast == slow) { break; } } return fast; } } return null; } }
复杂度分析
时间复杂度:O(N), 遍历2次,大概是 2N次循环 所以是 O(N)
空间复杂度:O(1) , 常数级,只有2个指针