给一个长度为n链表,若其中包含环,请找出该链表的环的入口结点,否则,返回null。
数据范围:
,
要求:空间复杂度
,时间复杂度
例如,输入{1,2},{3,4,5}时,对应的环形链表如下图所示:
可以看到环的入口结点的结点值为3,所以返回结点值为3的结点。
输入分为2段,第一段是入环前的链表部分,第二段是链表环的部分,后台会根据第二段是否为空将这两段组装成一个无环或者有环单链表
返回链表的环的入口结点即可,我们后台程序会打印这个结点对应的结点值;若没有,则返回对应编程语言的空结点即可。
{1,2},{3,4,5}
3
返回环形链表入口结点,我们后台程序会打印该环形链表入口结点对应的结点值,即3
{1},{}
"null"
没有环,返回对应编程语言的空结点,后台程序会打印"null"
{},{2}
2
环的部分只有一个结点,所以返回该环形链表入口结点,后台程序打印该结点对应的结点值,即2
public ListNode EntryNodeOfLoop(ListNode pHead) { ListNode const_tail = new ListNode(999); while(pHead != null){ if(pHead.next == const_tail) return pHead; ListNode temp = pHead.next; pHead.next = const_tail; pHead = temp; } return null; }不破坏链表
public ListNode EntryNodeOfLoop(ListNode pHead) { HashSet<ListNode> list = new HashSet<>(); while(pHead != null){ if(list.contains(pHead.next)) return pHead.next; list.add(pHead); pHead = pHead.next; } return null; }
import java.util.*; /* public class ListNode { int val; ListNode next = null; ListNode(int val) { this.val = val; } } */ public class Solution { public ListNode EntryNodeOfLoop(ListNode pHead) { // 左程云老师讲过这道题,采用快慢指针,快指针一次走两个结点,慢指针一次走一个结点,若二者能相遇,则有环,否则无环 // 若有环,在快慢指针相遇之后,令快指针返回head,降速成一次走一个结点,当二者再次相遇的结点就是成环结点 // 算法的时间复杂度O(N),额外空间复杂度O(1) // 1.先判断链表为空的情况 if (pHead == null) { return null; } // 2.确定链表不为空 ListNode fast = pHead; ListNode slow = pHead; boolean hasCycle = false; // 注意!这里不能用while而应该用do while // 因为fast和slow初始化就是相同的,用while会直接跳出循环 do { // 3.如果链表是无环的,那么fast一定先走到null,直接return false即可 if (fast.next == null || fast.next.next == null) { // 这里一定不会报空指针异常,因为先判断fast.next == null,若满足,则直接return false,不会继续判断后面 return null; } // 4.快指针一次走两个结点,慢指针一次走一个结点 fast = fast.next.next; slow = slow.next; } while (fast != slow); // 5.若因为fast == slow而结束循环,说明fast和slow二者相遇了,说明链表有环 // 若有环,则令快指针返回head,降速成一次走一个结点,当二者再次相遇的结点就是成环结点 fast = pHead; // 注意!这里不能用do while而应该用while // 假如成环结点就是pHead,用do while会错过一次判断 while (fast != slow) { // 6.快指针降速为一次走一个结点,慢指针依然是一次走一个结点 fast = fast.next; slow = slow.next; } return fast; } }
public class Solution {
public ListNode EntryNodeOfLoop(ListNode pHead) { SetListNode> set = new HashSet(); while(pHead != null){ if(!set.add(pHead)){ break; } pHead = pHead.next; } return pHead; }
}
public class Solution { //链表头到环入口的距离=相遇点到环入口的距离+(k-1)圈环长度 //判断有没有环,返回相遇的地方 public ListNode hasCycle(ListNode head) { //先判断链表为空的情况 if (head == null) return null; //快慢双指针 ListNode fast = head; ListNode slow = head; //如果没环快指针会先到链表尾 while (fast != null && fast.next != null) { //快指针移动两步 fast = fast.next.next; //慢指针移动一步 slow = slow.next; //相遇则有环,返回相遇的位置 if (fast == slow) return slow; } //到末尾说明没有环,返回null return null; } public ListNode EntryNodeOfLoop(ListNode pHead) { ListNode slow = hasCycle(pHead); //没有环 if (slow == null) return null; //快指针回到表头 ListNode fast = pHead; //再次相遇即是环入口 while (fast != slow) { fast = fast.next; slow = slow.next; } return slow; } }
import java.util.*; /* public class ListNode { int val; ListNode next = null; ListNode(int val) { this.val = val; } } */ public class Solution { public ListNode EntryNodeOfLoop(ListNode pHead) { //如果没有节点或者只有一个节点,就不可能有环入口,直接返回null if (pHead == null || pHead.next == null){ return null; } //我定义了一个快指针,一个慢指针,快指针每次跑两个节点,慢指针每次跑一个节点 ListNode fast = pHead; ListNode low = pHead; while (fast.next != null && fast.next.next != null){ low = low.next; fast = fast.next.next; if (low == fast){ //说明有环路 low = pHead; //让慢指针重新指向头节点,这次快慢指针每次都跑一个节点 while(low != fast){ low = low.next; fast = fast.next; } return low; } } //如果能走到这里,就说明没有环路,也返回null return null; } }
import java.util.*; /* public class ListNode { int val; ListNode next = null; ListNode(int val) { this.val = val; } } */ public class Solution { public ListNode EntryNodeOfLoop(ListNode pHead) { while(pHead != null && pHead.val > 0){ pHead.val = -pHead.val; pHead = pHead.next; } if(pHead == null){ return null; }else{ pHead.val = -pHead.val; return pHead; } } }
import java.util.*; /* public class ListNode { int val; ListNode next = null; ListNode(int val) { this.val = val; } } */ public class Solution { public ListNode EntryNodeOfLoop(ListNode pHead) { while(pHead != null){ if(pHead.val < 0){ pHead.val = pHead.val + Integer.MAX_VALUE; return pHead; }else{ pHead.val = pHead.val - Integer.MAX_VALUE; pHead = pHead.next; } } return null; } } 思路:走过更新数据为负数,当值为负数表示有环,把数还原回来,在这里使用了int最大值
public class Solution { public ListNode EntryNodeOfLoop(ListNode pHead) { HashSet<ListNode> hasCycleSet = new HashSet<>(); while(pHead != null){ if(hasCycleSet.contains(pHead)){ // 下一个节点已记录 return pHead; }else{ // 下一个节点未记录 hasCycleSet.add(pHead); } pHead = pHead.next; } return null; } }
当第二个指针指向环的入口结点时,第一个指针已经围绕着环走了一圈又回到了入口结点。