题目描述
给一个链表,若其中包含环,请找出该链表的环的入口结点,否则,返回null。
输入描述:输入分为2段,第一段是入环前的链表部分,第二段是链表环的部分,后台将这2个会组装成一个有环或者无环单链表
返回值描述:返回链表的环的入口结点即可。而我们后台程序会打印这个节点
示例1
输入:{1,2},{3,4,5}
返回值:3
说明:返回环形链表入口节点,我们后台会打印该环形链表入口节点,即3
题目分析
有环的链表,在遍历时会在环中一直循环,想要获得环的入口结点,直观地想,可以使用hash法保存出现的结点,当重复环的遍历过程时,第一次碰到重复的结点即为环入口结点B。
解题思路
方法1:hash法,记录第一次重复的结点
通过使用set或者map来存储已经遍历过的结点,当第一次出现重复的结点时,即为入口结点。
方法2:快慢指针
通过定义slow和fast指针,slow每走一步,fast走两步,若是有环,则一定会在环的某个结点处相遇(slow == fast),根据下图分析计算,可知从相遇处到入口结点的距离与头结点与入口结点的距离相同。
代码实现
方法1:hash法,记录第一次重复的结点
public ListNode EntryNodeOfLoop(ListNode pHead) { // 使用set来记录出现的结点 HashSet<ListNode> set = new HashSet<>(); while(pHead != null){ // 当set中包含结点,说明第一次出现重复的结点,即环的入口结点 if(set.contains(pHead)){ return pHead; } // set中加入未重复的结点 set.add(pHead); pHead = pHead.next; } return null; }
时间复杂度:O(n),需要将链表的所有结点遍历一遍,时间复杂度为O(n);
空间复杂度:O(n),需要额外的n空间的hashset来存储结点。
方法2:快慢指针
public ListNode EntryNodeOfLoop(ListNode pHead) { if(pHead == null) return null; // 定义快慢指针 ListNode slow = pHead; ListNode fast = pHead; while(fast != null && fast.next != null){ // 快指针是满指针的两倍速度 fast = fast.next.next; slow = slow.next; // 记录快慢指针第一次相遇的结点 if(slow == fast) break; } // 若是快指针指向null,则不存在环 if(fast == null || fast.next == null) return null; // 重新指向链表头部 fast = pHead; // 与第一次相遇的结点相同速度出发,相遇结点为入口结点 while(fast != slow){ fast = fast.next; slow = slow.next; } return fast; }
时间复杂度:O(n),需要将链表的所有结点遍历一遍,时间复杂度为O(n);
空间复杂度:O(1),需要额外两个快慢指针来遍历结点。