环形链表
可以使用两个指针,快指针每次走2步,慢指针每次走1步,如果链表有环,那么两个指针一定可以在链表中的某个节点相遇。
1 | /** |
2 | * Definition for singly-linked list. |
3 | * class ListNode { |
4 | * int val; |
5 | * ListNode next; |
6 | * ListNode(int x) { |
7 | * val = x; |
8 | * next = null; |
9 | * } |
10 | * } |
11 | */ |
12 | public class Solution { |
13 | public boolean hasCycle(ListNode head) { |
14 | if(head==null || head.next==null){ |
15 | return false; |
16 | } |
17 | ListNode fast = head, slow = head; |
18 | while(fast != null){ |
19 | if(fast.next != null){ |
20 | fast = fast.next.next; |
21 | }else{ |
22 | // fast到达链表末尾 |
23 | return false; |
24 | } |
25 | slow = slow.next; |
26 | if(fast == slow){ |
27 | return true; |
28 | } |
29 | } |
30 | return false; |
31 | } |
32 | } |
复杂度分析:
- 时间复杂度:$O(N)$。
- 空间复杂度:$O(1)$。
我们依然可以使用两个指针$fast$和$slow$。$fast$每次前进两步,$slow$每次前进一步,如果链表中有环,那么两个指针就会相遇,考虑此时相遇的情况如下图。
设此时链表头到环入口的距离为$a$,$slow$指针进入环后又走了距离$b$与$fast$指针相遇,假设此时$fast$指针已经走了$n$圈,那么$fast$指针走的总距离为$a+n(b+c)+b=a+(n+1)b+nc$。$slow$指针走的距离为$a+b$。由题可知,$fast$走的距离是$slow$走的距离的两倍,那么就能得到$a+(n+1)b+nc=2(a+b)=>a = c+(n-1)(b+c)$。根据$a$和$c$的等价关系可以发现从相遇点到入环点的距离再加上$n-1$圈长就等于从链表头到入环点的距离。此时再设一个指针$ptr$从链表头出发,每次前进一步,继续移动$slow$指针,那么两个指针就会在入环口相遇。
1 | /** |
2 | * Definition for singly-linked list. |
3 | * class ListNode { |
4 | * int val; |
5 | * ListNode next; |
6 | * ListNode(int x) { |
7 | * val = x; |
8 | * next = null; |
9 | * } |
10 | * } |
11 | */ |
12 | public class Solution { |
13 | public ListNode detectCycle(ListNode head) { |
14 | if(head == null){ |
15 | return null; |
16 | } |
17 | ListNode fast=head,slow=head; |
18 | while(fast != null){ |
19 | if(fast.next != null){ |
20 | fast = fast.next.next; |
21 | }else{ |
22 | return null; |
23 | } |
24 | slow = slow.next; |
25 | if(fast == slow){ |
26 | ListNode ptr = head; |
27 | while(ptr != slow){ |
28 | ptr = ptr.next; |
29 | slow = slow.next; |
30 | } |
31 | return ptr; |
32 | } |
33 | } |
34 | return null; |
35 | } |
36 | } |
复杂度分析:
- 时间复杂度:$O(N)$。
- 空间复杂度:$O(1)$。