题目描述
// 141. 环形链表
// 给定一个链表,判断链表中是否有环。
// 如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环
// 。 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位
// 置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。注意:pos 不作
// 为参数进行传递,仅仅是为了标识链表的实际情况。
// 如果链表中存在环,则返回 true 。 否则,返回 false 。
//
// 进阶:
// 你能用 O(1)(即,常量)内存解决此问题吗?
题解
/**
* Definition for singly-linked list.
* class ListNode {
* int val;
* ListNode next;
* ListNode(int x) {
* val = x;
* next = null;
* }
* }
*/
// 双指针 - 快慢针
// 如果链表存在环,我们让一个指针cur一次走2步,一个指针pre一次走1步,
// cur和pre一定会相遇,即cur==pre,这就是快慢针。
// 思路不难,难在如何处理边界条件,我们需先让cur初始化到head.next的位置。
// 那么我们得先保证head是有next的,所以head或者head.next为null,直接返回false
//
// 然后开始while循环,如果cur与pre相等(cur和pre相遇),while循环终止,
// 接下来需要cur走两步,pre走一步,cur走两步就是cur = cur.next.next,
// 他能走两步前提是cur走的第一步不是null,所以cur.next不能为null,
// 他能走一步前提是cur本身不是null,所以cur不能为null,所以如果cur或者
// cur.next为null,说明链表肯定不成环(其实链表只要出现了null,说明一定
// 不成环),直接返回false。否则,令cur走2步,pre走一步,如此循环,
// 直到cur在前面遇到了null,或者cur和pre相遇,链表有环,退出while循环返
// 回true。
//
// 执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户
// 内存消耗:39.7 MB, 在所有 Java 提交中击败了27.37%的用户
public class Solution {
public boolean hasCycle(ListNode head) {
if (head == null || head.next == null)
return false;
ListNode cur = head.next;
ListNode pre = head;
while (cur != pre) {
if (cur == null || cur.next == null)
return false;
cur = cur.next.next;
pre = pre.next;
}
return true;
}
}
// 自己测如下:
public class Main {
static class ListNode {
int val;
ListNode next;
ListNode(int x) {
val = x;
next = null;
}
}
public static boolean hasCycle(ListNode head) {
if (head == null || head.next == null)
return false;
ListNode cur = head.next;
ListNode pre = head;
while (cur != pre) {
if (cur == null || cur.next == null)
return false;
cur = cur.next.next;
pre = pre.next;
}
return true;
}
public static void main(String[] args) {
ListNode root1 = new ListNode(-10);
root1.next = new ListNode(9);
root1.next.next = new ListNode(2);
ListNode pos = root1.next.next;
root1.next.next.next = new ListNode(3);
root1.next.next.next.next = pos;
System.out.println(hasCycle(root1));
}
}
番外:
// 快慢针能不能一个指针走1步,一个指针走3步?
// 答案是可以的!只需要调整cur起始位置,和循环终止条件就行了,如下:
// 执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户
// 内存消耗:39.6 MB, 在所有 Java 提交中击败了38.27%的用户
public class Solution {
public boolean hasCycle(ListNode head) {
if (head == null || head.next == null || head.next.next == null)
return false;
ListNode cur = head.next.next;
ListNode pre = head;
while (cur != pre) {
if (cur == null || cur.next == null || cur.next.next == null)
return false;
cur = cur.next.next.next;
pre = pre.next;
}
return true;
}
}
// 注意,走3步的情况,cur起始位置不能为cur.next,这样会出现cur和pre在环
// 中永远不相遇的情况。如实例:[1,2] 0
// 一个走1步,一个走4步呢?
// 也是可以的!
// 执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户
// 内存消耗:39.8 MB, 在所有 Java 提交中击败了11.56%的用户
public class Solution {
public boolean hasCycle(ListNode head) {
if (head == null || head.next == null || head.next.next == null || head.next.next.next == null)
return false;
ListNode cur = head.next.next.next;
ListNode pre = head;
while (cur != pre) {
if (cur == null || cur.next == null || cur.next.next == null || cur.next.next.next == null)
return false;
cur = cur.next.next.next.next;
pre = pre.next;
}
return true;
}
}
// 一个走1步,一个走5步?
// 执行用时:1 ms, 在所有 Java 提交中击败了26.62%的用户
// 内存消耗:39.7 MB, 在所有 Java 提交中击败了28.04%的用户
public class Solution {
public boolean hasCycle(ListNode head) {
if (head == null || head.next == null || head.next.next == null || head.next.next.next == null || head.next.next.next.next == null)
return false;
ListNode cur = head.next.next.next.next;
ListNode pre = head;
while (cur != pre) {
if (cur == null || cur.next == null || cur.next.next == null || cur.next.next.next == null || cur.next.next.next.next == null)
return false;
cur = cur.next.next.next.next.next;
pre = pre.next;
}
return true;
}
}
// ... ...
// 一个走2步,一个走3步呢?也是可以的
// 执行用时:0 ms, 在所有 Java 提交中击败了100.00%的用户
// 内存消耗:39.7 MB, 在所有 Java 提交中击败了30.64%的用户
public class Solution {
public boolean hasCycle(ListNode head) {
if (head == null || head.next == null || head.next.next == null)
return false;
ListNode cur = head.next.next;
ListNode pre = head.next;
while (cur != pre) {
if (cur == null || cur.next == null || cur.next.next == null)
return false;
cur = cur.next.next.next;
pre = pre.next.next;
}
return true;
}
}
所以快慢针的核心在于一个快一个慢,而不拘泥于走几步。