题目链接:141. 环形链表
解法一 暴力法(哈希表)
思路
- 遍历链表,利用哈希表存储指针指向节点的地址。
- 判断指针指向节点的地址是否在哈希表中存在,若存在,直接返回true,否则,指针向后移动,继续遍历。
- 若指针指向空,返回false。
public boolean hasCycle(ListNode head) {
ListNode cur = head;
HashSet<ListNode> set = new HashSet<>();
while (!set.contains(cur) && cur != null){
set.add(cur);
cur = cur.next;
}
return cur != null;
}
复杂度分析
- 时间复杂度:O(n)
- 空间复杂度:O(n),建立了哈希表,开辟了额外空间
解法二 双指针(快慢指针)
思路
快慢指针,可以理解为两个速度不一样的运动员在沿着环形跑道追赶。
假设跑道长度为 N N N ,运动员A的速度为 x x x ,运动员B的速度为 y y y ,运动员B起点比运动员A靠前 k k k 个单位长度,两者同时出发,最终能够相遇吗?答案是肯定的,只需要满足等式 y T = x T + N − k yT = xT + N - k yT=xT+N−k,式子表示 T T T 时刻两者相遇。
具体的,我们令 x = 1 , y = 2 x = 1, y = 2 x=1,y=2,代入上式,可求得相遇时间 T = N − k T = N - k T=N−k。
- 初始化双指针,并定义起点位置,快慢指针的速度分别为2和1。
- 若链表中存在环,则两指针必定相遇,若不存在,则需要注意快指针是否已经指向空。
public boolean hasCycle(ListNode head) {
if (head == null)
return false;
ListNode slow = head;
ListNode fast = head.next;
while (slow != fast){
if (fast == null || fast.next == null)
return false;
fast = fast.next.next;
slow = slow.next;
}
return true;
}
复杂度分析
- 时间复杂度:
- 若链表不存在环,时间复杂度为O(n)
- 若链表存在环,环的长度为N,非环部分为M,则在非环部分首先迭代了M次。接着,进入环部分,假设此时快指针领先k个单位长度,k的取值范围为[0, N],则进行(N - k)次迭代,一共迭代(M + N - K)次,时间复杂度为O(M + N - K),即为O(n)
- 综上,时间复杂度为O(n)
- 空间复杂度:O(1)
小结
对付链表,直观的三个思路:
- 单指针(暴力法,直观,但浪费点空间)
- 双指针(省空间,思路略复杂,骚操作比较多)
- 递归(毕竟链表结构是递归的,Emmmm~但是难想)