问题
Given a linked list, determine if it has a cycle in it.
To represent a cycle in the given linked list, we use an integer pos which represents the position (0-indexed) in the linked list where tail connects to. If pos is -1, then there is no cycle in the linked list.
思路
常规思路
遍历链表,遍历过的节点存起来,若next节点已经被遍历过,则存在环;时间复杂度=O(n),空间复杂度O(n)
代码
public boolean hasCycle_1(ListNode head) {
if(head == null){
return false;
}
Set<ListNode> nodeSet = new HashSet<>();
ListNode f = head;
while (f.next != null) {
if (nodeSet.contains(f)) {
return true;
}
nodeSet.add(f);
f = f.next;
}
return false;
}
优化解法
没必要将遍历过的节点都存起来,只要设置一个标志位即可。新建一个flagNode,遍历过的节点都指向它,节省存储空间;空间复杂度=O(1),但是坏处是修改了节点; 类似“过河拆桥”。
public boolean hasCycle_3(ListNode head) {
if(head == null || head.next == null){
return false;
}
ListNode flagNode = new ListNode(-1);
ListNode f = head;
while (f.next != null) {
if (f.next == flagNode) {
return true;
}
ListNode tmp = f;
f = f.next;
tmp.next = flagNode;
}
return false;
}
更进一步,不修改节点数据,且空间复杂度为O(1)
意味着不能存储已经遍历过的节点,可以采用快慢指针法,若无环,则fast一定先到达null;若存在环,则fast一定会碰到 slow;fast 只要比slow快即可,这里用的是2倍速 vs 1倍速
代码
public boolean hasCycle_4(ListNode head) {
if(head == null || head.next == null) {
return false;
}
ListNode slow = head;
ListNode fast = head.next;
while (fast != null && fast.next != null) {
if(fast.next == slow || fast.next.next == slow){
return true;
}
fast = fast.next.next;
slow = slow.next;
}
return false;
}
知识点
链表的遍历、快慢指针