141 & 142. Linked List Cycle I/II

题干

141. Linked List Cycle
Given a linked list, determine if it has a cycle in it.
给予一个链表,返回这个链表是否存在循环。
142. Linked List Cycle II
Given a linked list, return the node where the cycle begins. If there is no cycle, return null.
Note: Do not modify the linked list.

给予一个链表,返回这个链表从哪个节点开始循环,如果链表中没有循环,则返回null。

解题思路

链表存在循环,指的是链表中某一个节点的next指针指向了过去的一个节点。
那么我们如何来判断一个链表是否存在循环呢?
比较蠢的一个办法,判断一个节点是否有出现过。
不能单独靠val来判断节点是否出现,因为题干里没有说所有的val都是unique的。
但是每个节点的指针是独特的,只要把遍历过的指针存到一个数组中,如果某个指针出现了第二次,那说明这个数组就是重复的。
但是吧,这样就太不优雅了,我们有没有什么办法不利用额外的空间来完成这个问题呢?
答案是有的:快慢指针。
我们可以设计两个游标指针,步长并不相同,如果两者正好能指向同一个节点,那么就说明链表中存在循环。

var hasCycle = function(head) {
    if(head==null){
        return false;
    }
    let slow = head;
    let fast = head;
    while(true){
        slow = slow.next;
        if(!fast||!fast.next){
            return false;
        }
        fast = fast.next.next;
        if(slow==fast){
            return true;
        }
    }
    return true;
};

这样第一题就解决了,第二题还远吗?
在第一题中我们设慢指针步长为1,快指针步长为2,这个时候当经过多少步后,指针会指向同一个节点呢?
做一个简单的数学算式,设链表不参加循环的长度为a,链表循环部分长度为b,步数为x。
那么可以推得:x+b = 2x
经过的步数就等于慢指针走过的步数,这里我们可以推出循环部分的链表的长度,知道了这个长度以后,我们可以再设计一对快慢指针,这次两个指针步长相同,但是快指针会先走出b步,这样当快指针走到链表末尾时,慢指针正好指向了非循环部分的最后一个节点。而两个指针各再向前一步,得到的则是循环开始的节点。
而在刚才的程序中,慢指针正好是那个已经走出了b步的指针,我们只需要将快指针指向头节点,然后循环步长为1即可。
而对于不存在循环的链表来说,将上题中的return false的部分改为return null即可。

var detectCycle = function(head) {
    if(head==null){
        return false;
    }
    let slow = head;
    let fast = head;
    while(slow!=fast){
        slow = slow.next;
        if(!fast||!fast.next){
            return null;
        }
        fast = fast.next.next;
    }
    fast = head;
    while(fast!=slow){
        fast = fast.next;
        slow = slow.next;
    }
    return slow;
};

你可能感兴趣的:(141 & 142. Linked List Cycle I/II)