142. 环形链表 II
给定一个链表,判断链表中是否有环。
为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。
示例 1:
输入:head = [3,2,0,-4], pos = 1
输出:true
解释:链表中有一个环,其尾部连接到第二个节点。
示例 2:
输入:head = [1,2], pos = 0
输出:true
解释:链表中有一个环,其尾部连接到第一个节点。
示例 3:
输入:head = [1], pos = -1
输出:false
解释:链表中没有环。
-
1.set集合法
思路:可参考环形链表 LeetCode 141. 环形链表 - 简书
- 遍历该链表,将每个节点都放入set集合中
- 如果是环形链表在遍历过程中一定存在相同的节点,反之则不是环形链表
public static class ListNode {
private int val;
private ListNode next;
public ListNode(int val) {
this.val = val;
}
}
public boolean hasCycle(ListNode head) {
Set<ListNode> set = new HashSet<>();
while (head != null) {
if (set.contains(head)) {
return true;
} else {
set.add(head);
head = head.next;
}
}
return false;
}
复杂度分析:
时间复杂度:O(n),对于含有 n 个元素的链表,我们访问每个元素最多一次。添加一个结点到哈希表中只需要花费 O(1) 的时间。
空间复杂度:O(n),空间取决于添加到哈希表中的元素数目,最多可以添加 n 个元素。
-
2.Floyd 算法
思路:图解思路来源于leetcode官方题解
public ListNode detectCycle2(ListNode head) {
if (head == null) return null;
//寻找环的入口节点
ListNode intersect = getIntersect(head);
if (intersect == null) return null;
ListNode ptr1 = head;
ListNode ptr2 = intersect;
while (ptr1 != ptr2) {
ptr1 = ptr1.next;
ptr2 = ptr2.next;
}
return ptr1;
}
/**
* 获取相遇节点
* @param head
* @return
*/
public ListNode getIntersect(ListNode head) {
ListNode slow = head;
ListNode fast = head;
while (fast != null && fast.next != null) {
slow = slow.next;
fast = fast.next.next;
if (slow == fast) {
return fast;
}
}
return null;
}
复杂度分析:
时间复杂度:O(n),不管是哪一类链表,都会在与节点数成线性关系的时间内运行完。
空间复杂度:O(1),Floyd 的快慢指针算法仅需要几个指针,所以只需常数级别的额外空间。
-
源码
-
我会随时更新新的算法,并尽可能尝试不同解法,如果发现问题请指正
- Github