链表的环相关问题

2020-05-07  本文已影响0人  _空格键_

题目

给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null。
说明: 不允许修改给定的链表。

链表节点环图示

解题

方法 1:哈希表

使用一个 Set /List保存已经访问过的节点,我们可以遍历整个列表,检查每一个节点是否已存在Set中,如果存在即可返回第一个出现重复的节点,否则不存在环返回null。

1.1 算法

public class Solution {
    public ListNode detectCycle(ListNode head) {
        Set<ListNode> visited = new HashSet<ListNode>();

        ListNode node = head;
        while (node != null) {
            if (visited.contains(node)) {
                return node;
            }
            visited.add(node);
            node = node.next;
        }

        return null;
    }
}

1.2 复杂度分析

方法 2:Floyd 算法

也称双指针/快慢指针法。就是用两个指针,从头开始遍历,一个指针步进+1,一个指针步进+2,如果链表存在环,这两个指针总会相遇。想象下环形操场,两个人同起点同方向跑步,一个快一个慢,他们总会在某一点相遇,同理。

2.1 算法

本算法分两步:第一步检查是否存在环,第二部如果存在环,找出环的入口。

通过数学模型计算,第一次相遇后,快慢指针走过的距离 2S慢=S快,即

2(F + a) = F + N(a + b) + a
2F + 2a = F + 2a + b + (N - 1)(a + b)
          F = b + (N - 1)(a + b)

  • F是到达入口点长度
  • N为快指针跑过第几个整圈后会与慢指针相遇

下图便于理解


环算法模型
public class Solution {
    private ListNode getIntersect(ListNode head) {
        ListNode tortoise = head;
        ListNode hare = head;

        // 如果存在环,一定会相遇;如果不存在环,hare最先到达null
        while (hare != null && hare.next != null) {
            tortoise = tortoise.next;
            hare = hare.next.next;
            if (tortoise == hare) {
                return tortoise;
            }
        }

        return null;
    }

    public ListNode detectCycle(ListNode head) {
        if (head == null) {
            return null;
        }

        //寻找相遇点,如果存在环,返回相遇点;如果不存在,返回null
        ListNode intersect = getIntersect(head);
        if (intersect == null) {
            return null;
        }

        // 寻找入环点,需要两个指针+1速度,一个从头开始,一个从相遇点开始,两个指针相遇时即为环入口
        ListNode ptr1 = head;
        ListNode ptr2 = intersect;
        while (ptr1 != ptr2) {
            ptr1 = ptr1.next;
            ptr2 = ptr2.next;
        }

        return ptr1;
    }
}

2.2 复杂度分析


[注]
详细参考: LeetCode 142.环形链表 II

上一篇 下一篇

猜你喜欢

热点阅读