链表中的环

61 阅读2分钟

何为链表中的环?

通常情况下,链表是由多个节点组成的,每一个节点指向下一个节点,链表的最后一个节点会指向 null 。但如果链表中的某个节点指向了某个前节点,那么链表就会形成一个环,当遍历链表的时候,就会进入无限循环。

链表中环的影响

多数情况下,环在链表中被认为是错误的或者是不期望的状态,尤其是在单向链表中。一个节点的 next 指针被指向设置为先前的节点,从而会导致链表的无限循环。这种情况可能是编译错误、不正确的链表操作或数据结构的错误使用导致。 当环存在的时候,主要通常会有如下几个问题:

  1. 无限遍历:链表通常被用作可以顺序遍历的数据结构。一个环会导致遍历操作陷入无限循环,因为永远到达不了链表的末尾 null。
  2. 内存泄漏:在某些编程环境中,如果链表中存在环,那么标准的垃圾回收机制可能无法正确清理不再需要的节点,从而导致内存泄漏。
  3. 数据不易处理:环的存在使得标准的链表操作(如反转、合并、查找等)变得复杂甚至不可能正确执行。

但在某些特定的应用中,环也有一定的作用,例如循环链表,在需要遍历循环元素时非常有用。

如何判断链表中的环

检测重复状态通常是发现无限循环的关键,我们可以通过遍历链表来判断是否有节点被访问了不止一次,对于被访问的节点,我们可以用哈希表和双指针的方法来追踪。

哈希表法

将遍历的每一个节点都放入哈希表中,如果有节点被遍历超过一次,哈希表的特性会做出判断,则可以判断该链表是有环的链表。

/**
     * 方法1:通过HashMap判断
     *
     * @param head
     * @return
     */
    public static boolean hasCycleByMap(ListNode head) {
        Set<ListNode> seen = new HashSet<>();
        while (head != null) {
            if (!seen.add(head)) {
                return true;
            }
            head = head.next;
        }
        return false;
    }

双指针法

双指针方法就是用经典的快慢指针(快指针一次向前两步,慢指针一次向前一步),如果这个链表有环的话,那么快慢指针遍历N次链表后两个指针一定会相遇,这就像在操场上跑步时候“套圈”的原理一样。

/**
     * 方法2 通过双指针实现
     *
     * @param head
     * @return
     */
    public static boolean hasCycleByTwoPoint(ListNode head) {
        if (head == null || head.next == null) {
            return false;
        }
        ListNode fast = head;
        ListNode slow = head;
        while (fast != null && fast.next != null) {
            fast = fast.next.next;
            slow = slow.next;
            if (fast == slow)
                return true;
        }
        return false;
    }