判断单链表是否有环

1.判断链表是否有环

要判断单链表是否有环,可以设两个指针p和q,p每次移动一个节点,q每次移动两个节点,如果有环,则在p移动n个节点后与q相遇(想象两个人在同一环形跑道的起点跑步,A速度是B的两倍,则在A跑完一圈时B跑完两圈,他们在起点第一次相遇),如果无环,则q会先遇到NULL.

 

2.计算环的长度

记录pq指针第一次相遇的相遇点,另一指针从相遇点出发,再次遇到相遇点走过的距离即为环的长度

 

3.求连接点的位置 (重要)

这里有一个定理:相遇点到连接点的距离等于头结点到连接点的距离。 故可另pq指针分别从头结点和相遇点出发,pq相遇的结点即为连接点的位置

定理的简单证明思路:

设头结点到连接点的距离为a,环的长度为b, 则链表的长度为a+b.设第i步访问的节点用S(i)表示,

则有:i<a 时, S(i)=i

           i>=a时, S(i)=a+(i-a)%b

设pq结点走了x步后相遇,则有S(x)=S(2x),根据环的周期性可知:2x=x+tb ===>  x=tb,   又由于相遇只可能在环内,故x>=a

从而S(a)=S(a+tb)=S(x+a),   得出结论: 从碰撞点再走a步即为连接点,而从起始点再走a步也为连接点,故相遇点到连接点的距离等于头结点到连接点的距离

 

 

4.求链表的长度

环的长度加上头结点到连接点之间的距离即为链表的长度。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
 //Given a linked list, return the node where the cycle begins. If there is no cycle, return null. 
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        ListNode *p,*q;
        p=q=head;
        while(q!=NULL)
        {
            p=p->next;
            q=q->next;
            if(q==NULL)
                return NULL;
            q=q->next;
            if(p==q)
                break;
        }
        q=head;
        while(p!=q && p!=NULL && q!=NULL)
        {
            p=p->next;
            q=q->next;
        }
        if(p!=NULL && q!=NULL)
            return p;
        else
            return NULL;
    }
};

 

点赞