链表有环相关问题

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;
}

这里写图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值