【Leetcode】141. 环形链表

147 阅读4分钟

题目描述

在这里插入图片描述

// 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;
    }
}

所以快慢针的核心在于一个快一个慢,而不拘泥于走几步。