链表有环相关问题

来源:互联网 发布:戴墨镜叼烟修图软件 编辑:程序博客网 时间:2024/06/15 18:01

1、如何判断一个链表是否有环?
2、如果有环的话,如何计算环的长度?
3、如何找到环的入口?
4、找到两条链表的第一个交点

1、现在先来解决第一个问题:
可以设置两个指针fast和slow都指向链表头,fast指针每次向前走两步,slow指针每次向前走一步。
如果fast指针遇到NULL了,说明该链表没有环。
如果链表有环,fasr指针每次都比slow指针走的快,最终两个指针会相遇。

bool IsCircle(ListNode *head){    //链表为空或链表只有结点就不用判断了    if(head==NULL || head->m_pNext==NULL)        return false;    ListNode *slow = head;    ListNode *fast = head;    while(fast!=NULL && fast->m_pNext!=NULL)    {                   fast = fast->m_pNext->m_pNext;        slow = slow->m_pNext;           if(fast == slow)        {            break;        }       }    /*    跳出循环有两种情况:    1、要么是fast指针遇上NULL跳出循环,此时没环    2、有环,fast指针和slow指针相遇了    */    if(fast==NULL || fast->m_pNext==NULL)    {        return false;//第一种情况    }    else    {        return true;//第二种情况    }}ListNode *Search(ListNode *head,int key){    for(ListNode *p=head;p!=NULL;p=p->m_pNext)    {        if(p->m_nKey == key)            return p;    }    return NULL;}int main(){    //先测试没有环的链表    ListNode *head = NULL;    for(int i=0;i<10;i++)    {        InsertTail(&head,i);    }    Show(head);    bool flag = IsCircle(head);    if(!flag)        cout<<"没有环"<<endl;    else        cout<<"有环"<<endl;    //测试有环的链表    //构造环    ListNode *p = Search(head,2);    ListNode *q = Search(head,9);    q->m_pNext = p;    flag = IsCircle(head);    if(!flag)        cout<<"没有环";    else        cout<<"有环";    return 0;}

这里写图片描述

2、关于环的长度
从第一次相遇时开始计数,到第二次相遇时结束计数

int GetCircleLength(ListNode *head){    ListNode *fast = head;    ListNode *slow = head;    int count = 0;//环的长度    int time = 0;//第几次相遇    while(1)    {        fast = fast->m_pNext->m_pNext;        slow = slow->m_pNext;        if(fast == slow)        {            time++;        }        if(time == 1)//开始计数        {            count++;        }        if(time == 2)//第二次相遇,结束计数            return count;    }    return -1;}int main(){    ListNode *head = NULL;    for(int i=0;i<10;i++)    {        InsertTail(&head,i);    }    //构造环    ListNode *p = Search(head,2);    ListNode *q = Search(head,9);    q->m_pNext = p;    cout<<"环的长度:"<<GetCircleLength(head)<<endl;    return 0;}

这里写图片描述

3、环的入口点

这里写图片描述

这里写图片描述
碰撞点p到连接点的距离=头指针到连接点的距离,因此,分别从碰撞点、头指针开始走,相遇的那个点就是连接点。
(1)当fast与slow相遇时,show肯定没有走完链表,而fast已经在还里走了n(n>= 1)圈。假设slow走了s步,那么fast走了2s步。fast的步数还等于s走的加上环里转的n圈,所以有:
2s = s + nr。因此,s = nr。
(2)设整个链表长为L,入口据相遇点X,起点到入口的距离为a。因为slow指针并没有走完一圈,所以:
a + x = s,带入第一步的结果,有:a + x = nr = (n-1)r + r = (n-1)r + L - a;即:
a = (n-1)r + L -a -x;
这说明:从头结点到入口的距离,等于转了(n-1)圈以后,相遇点到入口的距离。因此,我们可以在链表头、相遇点各设一个指针,每次各走一步,两个指针必定相遇,且相遇第一点为环入口点。

ListNode * findCircleEntrance(ListNode *head){    ListNode *fast = head;    ListNode *slow = head;    while(fast!=NULL && fast->m_pNext!=NULL)    {        fast = fast->m_pNext->m_pNext;        slow = slow->m_pNext;        if(fast == slow)        {            break;        }    }    if(fast==NULL || fast->m_pNext==NULL)        return NULL;//说明没环    slow = head;    while(slow != fast)    {        slow = slow->m_pNext;        fast = fast->m_pNext;    }    return slow;}int main(){    ListNode *head = NULL;    for(int i=0;i<10;i++)    {        InsertTail(&head,i);    }    //构造环    ListNode *p = Search(head,2);    ListNode *q = Search(head,9);    q->m_pNext = p;    ListNode *s = findCircleEntrance(head);    if(s != NULL)        cout<<"环的入口:"<<s->m_nKey<<endl;    return 0;}

这里写图片描述

///////////////////////////////
4、找到两条链表的第一个交点?????????????
(1)首先要判断两条链表是否有环,如果一条链表有环,一条链表没环,那肯定就没有交点了
(2)如果链表有环,断开环(断开环之前要保存下一个结点的值,因为之后还要还原)
(3)现在两条链表都没有环,求出两个链表的长度。
(4)将两条链表相差的长度先走完,两个链表再一起走,相遇的第一点即为第一个交点
(5)还原环,判断是否有交点

int GetLength(ListNode *head)//得到链表长度{    int count = 0;    for(ListNode *p=head;p!=NULL;p=p->m_pNext)        count++;    return count;}ListNode *FirstIntersection(ListNode *head1,                            ListNode *head2){    //判断head1和head2是否有环    ListNode *p = findCircleEntrance(head1);    ListNode *q = findCircleEntrance(head2);    //一个有环另一个没环则一定不相交    if( (p==NULL && q!=NULL) || (p!=NULL && q==NULL))        return NULL;    //用于保存断开环之前入口的下一个结点    ListNode *pnext = NULL;    ListNode *qnext = NULL;    if(p != NULL)//p有环则将环断开,之后需要还原    {        pnext = p->m_pNext;        p->m_pNext = NULL;    }    if(q != NULL)//q有环则将环断开,之后需要还原    {        qnext = q->m_pNext;        q->m_pNext = NULL;    }    //两个链表都没有环    int len1 = GetLength(head1);    int len2 = GetLength(head2);    ListNode *r = head1->m_pNext;    ListNode *s = head2->m_pNext;    /*将head1和head2的多余的长度先走完,两个链表再一起走,    相遇的第一点即为第一个交点*/    while(len1 > len2)    {        r = r->m_pNext;        len1--;    }    while(len2 > len1)    {        s = s->m_pNext;        len2--;    }    //多余的长度走完了,两个链表一起走,查找交点    while(r!=NULL && s!=NULL)    {        if(r == s)        {            break;        }        r = r->m_pNext;        s = s->m_pNext;    }    //将断开的环还原    if(pnext != NULL)    {        p->m_pNext = pnext;    }    if(qnext != NULL)    {        q->m_pNext = qnext;    }    //判断是否有交点    if(r==s && r!=NULL)        return r;    else        return NULL;}int main(){    //1 先测试两条不相交的链表(没有环)    ListNode *head1 = NULL;    ListNode *head2 = NULL;    for(int i=0;i<10;i++)//构造两条不相交的链表    {        InsertTail(&head1,i);        InsertTail(&head2,i+20);    }    Show(head1);    Show(head2);    cout<<"两条无环不相交的链表:";    ListNode *p = FirstIntersection(head1,head2);    if(p == NULL)        cout<<"没有交点"<<endl;    else        cout<<"第一个交点为"<<p->m_nKey<<endl;    cout<<endl<<endl;    //2 构造环 测试一个有环的链表和一个没环的链表    ListNode *r = Search(head1,2);    ListNode *s = Search(head1,9);    s->m_pNext = r;    cout<<"一个有环的链表和一个没环的链表:";    p = FirstIntersection(head1,head2);    if(p == NULL)        cout<<"没有交点"<<endl;    else        cout<<"第一个交点为"<<p->m_nKey<<endl;    //看环是否还原        bool flag = IsCircle(head1);    if(!flag)        cout<<"没有环"<<endl;    else        cout<<"有环"<<endl;    cout<<endl<<endl;    //3 测试两条没有环,有交点的链表    ListNode *head3 = NULL;    ListNode *head4 = NULL;    for(int i=0;i<10;i++)//构造两条不相交的链表    {        InsertTail(&head3,i);    }    InsertTail(&head4,20);    InsertTail(&head4,21);    p = Search(head3,3);    ListNode * q = Search(head4,20);    assert(p!=NULL && q!=NULL && q->m_pNext!=NULL);    q->m_pNext = p;//3为两条链的第一个交点    cout<<"两条没有环,有交点的链表:";    p = FirstIntersection(head3,head4);    if(p == NULL)        cout<<"没有交点"<<endl;    else        cout<<"第一个交点为"<<p->m_nKey<<endl;    Show(head3);    Show(head4);    cout<<endl<<endl;    //构造两个个相交的且有环的链表    p = Search(head3,5);    q = Search(head3,9);    assert(p!=NULL && q!=NULL && q->m_pNext==NULL);    q->m_pNext = p;  //构造环    cout<<"两条有环,有交点的链:";    p = FirstIntersection(head3,head4);    if(p == NULL)        cout<<"没有交点"<<endl;    else        cout<<"第一个交点为"<<p->m_nKey<<endl;    return 0;}

这里写图片描述

原创粉丝点击