Leetcode刷题系列:环形链表

306 阅读3分钟

这是我参与8月更文挑战的第3天,活动详情查看:8月更文挑战

题目

给定一个链表,判断链表中是否有环。

如果链表中有某个节点,可以通过连续跟踪 next 指针再次到达,则链表中存在环。 为了表示给定链表中的环,我们使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos-1,则在该链表中没有环。注意:pos 不作为参数进行传递,仅仅是为了标识链表的实际情况。

如果链表中存在环,则返回 true 。 否则,返回 false

进阶: 你能用 O(1)(即,常量)内存解决此问题吗?

来源:力扣(LeetCode) 链接:leetcode-cn.com/problems/li…

实例:

示例 1:

1.png

输入:head = [3,2,0,-4], pos = 1 输出:true 解释:链表中有一个环,其尾部连接到第二个节点。

示例 2:

2.png

输入:head = [1,2], pos = 0 输出:true 解释:链表中有一个环,其尾部连接到第一个节点。

示例 3:

3.png

输入:head = [1], pos = -1 输出:false 解释:链表中没有环。

实现方案1 :暴利破解:二次访问

可以理解为检测链表的某节点能否二次到达(重复访问)的问题。

  • 实现逻辑:

    • 创建一个 数组容器 记录已经访问过的节点
    • 每次访问到新的节点,都与容器中的记录进行匹配,若相同则存在环
    • 若匹配之后没有相同节点,则存入容器,继续访问新的节点
    • 直到访问节点的next指针返回null,或者当前节点与容器的某个记录相同,操作结束
  • 核心:

    • 数据结构:数组
    • 算法思维:遍历
public boolean hasCycle1(ListNode head) {

        ListNode[] array = new ListNode[10000];
        while (head != null) {
            // 循环遍历容器
            for (int i = 0; i < array.length; i++) {
                // 并与容器中的节点进行比较,已经存在过,则是环形链表
                if (array[i] == head) {
                    return true;
                }
                // 不存在容器中的话,将节点信息,存入容器
                if (array[i] == null) {
                    array[i] = head;
                    break;
                }
            }
            // 向下指定 head
            head = head.next;
        }
        return false;
    }

实现方案2 :set集合 去重

image.png

Java 原生 不就提供了 可以去重的 Set 集合、既然这样尝试使用Set 来完成一下吧~~~! 其逻辑和 暴利破解一样

   public boolean hasCycle2(ListNode head) {

        // 使用set集合
        Set<ListNode> array = new HashSet<>();
        while (head != null) {
            // 循环遍历 将节点 添加到集合中,添加失败,则说明节点已存在,是环形链表
            if (!array.add(head)) {
                return true;
            }
            // 向下指定 head
            head = head.next;
        }
        return false;
    }

实现方案3:快慢指针

环形连接:我们可以理解为 追击 问题 即可以想象成一个环,犹如操场跑步一样,跑的快的人 总能 绕一圈后追上 跑的慢的人。如果是直线,则不会存在 追击问题

实现逻辑:

  • 定义 两个变量
    • 一个 慢指针 slow = head 指向 第一节点 、 一个快指针 fast = head.next 指向 第二个节点
  • 快的每次 移动2步,慢的移动1步,
    • 即 fast 每次移动两个节点 fast.next.next 、 slow 每次移动一个节点 slow.next
  • 若:最终 fast == slow 则说明存在环
  • 若: fast == null \ fast.next != null 则 操作结束,说明不存在环

核心:

  • 数据结构:两个变量
  • 算法思维:遍历 + 快慢指针
public boolean hasCycle3(ListNode head) {

        if (head == null || head.next == null) {
            return false;
        }

        // 慢指针
        ListNode slow = head;
        // 快指针
        ListNode fast = head.next;
        while (fast != null && fast.next != null) {
            // 当快指针 等于 慢指针时, 则说明链表 发生了循环
            if (slow == fast) {
                return true;
            }

            // 慢指针 走1步
            slow = slow.next;
            // 快指针 走2步
            fast = fast.next.next;
        }
        return false;
    }

github 地址

案例地址