leetcode 142 环形链表 2

81 阅读1分钟

给定一个链表的头节点  head ,返回链表开始入环的第一个节点。 如果链表无环,则返回 null

这个题目有一种很简单的方法,就是直接利用set保存走过的路径,也就是经过的所有的顶点,一但发现走过重复的顶点就直接返回该顶点即可。

public ListNode1 detectCycle(ListNode1 head) {
    if (head == null || head.next == null) return null;
    Set<ListNode1> visited = new HashSet<>();
    while (head != null){
        if (visited.contains(head)){
            return head;
        }
        visited.add(head);
         head = head.next;
    }
    return null;
}

时间复杂度为O(n)也就是遍历一遍链表,空间复杂度为O(n)。

但是题目进阶要求时间复杂度为O(1),且不能改变链表,那么就只能考虑双指针(快慢指针方法)。解题思想就使用官方leetcode题解。

当f第一次遇到s,那么就设置f已经走了n圈,且走了环的b长度,链表起始点到入环点为a,那么就把f没有环剩下的距离设为c,那么f就走了a+(b+c)n+b;而且f还走了两倍的s也就是2(a+b),将二者相等就会得到a=c+(n-1)(b+c) => a=c(因为s和f肯定第一次遇见就在s第一次进入环中), 那么就代表需要求出的a就是s走完接下来一圈的步数,也就是重新定义一个“s”指针指向head,当它与s指针进行相遇那么该节点就是入环点。

代码如下:

//你是否可以使用 O(1) 空间解决此题?
public ListNode1 detectCycle1(ListNode1 head) {
    if (head == null && head.next == null) return null;
    ListNode1 s = head;
    ListNode1 f = head;
    while (f.next != null && f.next.next != null){
        s = s.next;
        f = f.next.next;
        if (s == f) break;
    }
    if (f.next == null || f.next.next == null) return null;
    ListNode1 pre = head;
    while (pre != s){
        pre = pre.next;
        s = s.next;
    }
    return pre;
}