剑指 Offer II 022. 链表中环的入口节点(142. 环形链表 II && 141. 环形链表)
题目:
思路:
【1】剑指 Offer II 022. 链表中环的入口节点(142. 环形链表 II )
【1.1】哈希表作为辅助空间的方式
【1.2】进阶的,不借助辅助空间,采用双指针的方式
双指针的原理: 当f与s在环内相遇时, s与f相对于入口起点的距离为c: f 总路程= a + xb + c (f已经走了x圈, x > y) s 总路程 = a + yb + c (s已经走了y圈, y >= 0) 由于 f 比 s移动快 所以 x > y, 所以假设 z = x - y; f 总路程 = a + (y + z)b + c = a + yb + c + zb; 所以 代入 s总路程 = a + yb +c 得到: f 总路程 = s 总路程 + zb (z为正整数) 所以 最后结论 第一次相遇时 f 比 s 多走 z 圈 所以此时两者任意一者重新从起点出发,再次相遇必然是环的入口
【2】141. 环形链表
本质上是借鉴上面进阶版本的双指针思路
用走两步的快指针和走一步的慢指针
如果快指针会变为null,则说明不存在环,会到底的
如果快指针=慢指针,则说明存在环,两者会相遇
代码展示:
【1】剑指 Offer II 022. 链表中环的入口节点(142. 环形链表 II ):
哈希表辅助空间的方式:
//时间3 ms击败21.36% //内存42.4 MB击败7.29% public class Solution { public ListNode detectCycle(ListNode head) { ListNode pos = head; Set<ListNode> visited = new HashSet<ListNode>(); while (pos != null) { //第一个重复的必然是环入口 if (visited.contains(pos)) { return pos; } else { visited.add(pos); } pos = pos.next; } return null; } }
双指针的方式:
//时间0 ms击败100% //内存41.6 MB击败70.14% /** * Definition for singly-linked list. * class ListNode { * int val; * ListNode next; * ListNode(int x) { * val = x; * next = null; * } * } */ public class Solution { public ListNode detectCycle(ListNode head) { ListNode fast = head, slow = head; //如果无环返回null while (true) { if (fast == null || fast.next == null) return null; fast = fast.next.next; slow = slow.next; if (fast == slow) break; } //如果有环输出环入口 fast = head; while (slow != fast) { slow = slow.next; fast = fast.next; } return fast; } }
【2】141. 环形链表
//时间0 ms 击败 100% //内存42.7 MB 击败 8.77% /** * Definition for singly-linked list. * class ListNode { * int val; * ListNode next; * ListNode(int x) { * val = x; * next = null; * } * } */ public class Solution { public boolean hasCycle(ListNode head) { ListNode fast = head, slow = head; while (true){ if (fast == null || fast.next == null) return false; fast = fast.next.next; slow = slow.next; if (fast == slow) break; } return true; } }