【单链表面试题】------链表相交问题

思考:
两个链表相交,其特点为两个链表共用一个尾;但这两个链表可分为以下两个情况:

情况1:两个链表不带环
情况2:两个链表可能带环

其中情况2又可分为以下几种问题:

  1. 两个链表都不带环
    和情况1是相同的(具体如图1)
  2. 一个链表带环,一个不带环
    结论:一定不相交
  3. 两个链表都带环
    a. 不相交
    b. 相交
    1)环内相交
    2)环外相交

分别来看这几种情况:

情况1: 两个链表不带环
问题1:链表是否相交

这里写图片描述

问题2:求解相交点

求解相交点时,只需让两个指针同时遍历两个链表,当两个两个指针相遇时的节点即为相交节点,但这样处理的条件是:两个链表的长度相同;当链表长度不相同时,只需让较长链表指针先走两链表长度相差的步数即可

这里写图片描述

情况2:两个链表可能带环

  1. 两个链表都不带环
    和情况1是相同的(具体看第一个图)

  2. 一个链表带环,一个不带环
    结论:一定不相交

  3. 两个链表都带环
    a. 不相交
    b. 相交
    1)环内相交
    2)环外相交

情景1:两个链表都不带环

转化为情况1的问题,具体请看情况1的分析

情景2:一个链表带环,一个链表不带环

一定不相交

这里写图片描述

情景3:两个都带环

问题1: 两个链表是否相交

这里写图片描述

问题2:两个在环内相交还是环外相交

这里写图片描述

问题3:链表相交点

a. 环外相交
环外相交,入口点相同,故而可转化为不带环链表的同类题,可将入口点视为两个链表不带环部分的尾,从而求的交点(具体过程可看问题2的解决方案)
b. 环内相交 环内相交,入口点不同,相遇点可能相同可能不同,故而没有条件可以借助求得相交点,在这里相交点不得而知

具体代码:

情况1:

判断链表是否相交

int SListIsCrossNode(SListNode* list1, SListNode* list2)
{
    assert(list1);
    assert(list2);
    while (list1 && list1->_next)
    {
        list1 = list1->_next;
    }
    while (list2 && list2->_next)
    {
        list2 = list2->_next;
    }
    //都走到尾,相同即相交
    if (list1 == list2)
        return 1;
    else
        return 0;
}

获取相交点

SListNode* GetCrossNode(SListNode* list1, SListNode* list2)
{
    assert(list1);
    assert(list2);
    size_t n1 = 0;
    size_t n2 = 0;
    SListNode* cur1 = list1;
    SListNode* cur2 = list2;
    //求链表1的长度
    while (cur1)
    {
        cur1 = cur1->_next;
        ++n1;
    }
    //求链表2的长度
    while (cur2)
    {
        cur2 = cur2->_next;
        ++n2;
    }

    //选出两个链表当中较长的一个链表
    size_t num = abs(n1 - n2);
    SListNode* longlist = list1;
    SListNode* shortlist = list2;
    if (n1 < n2)
    {
        longlist = list2;
        shortlist = list1;
    }

    //让较长的链表先走链表的长度差步
    while (num--)
    {
        longlist = longlist->_next;
    }

    //两个链表同时走
    while (longlist != shortlist)
    {
        longlist = longlist->_next;
        shortlist = shortlist->_next;
    }

    return longlist;

}

情况2:两个链表可能带环

是否相交:

//两个链表是否相交(可能带环)
int SListIsCrossNode1(SListNode* list1, SListNode* list2)
{
    assert(list1);
    assert(list2);


    //是否带环,若带环返回相遇点
    SListNode* meet1 = SListIsCircle(list1);
    SListNode* meet2 = SListIsCircle(list2);

    //如果带环,求入口点
    SListNode* entry1;
    SListNode* entry2;

    if (meet1 == NULL && meet2 == NULL)
    {
        //两个都不带环,转换为不带环的情况
        //相交返回1,不相交返回0
        return  SListIsCrossNode(list1, list2);

    }
    else if (meet1==NULL || meet2==NULL)
    {
        //一个带环,一个不带环,则一定不相交,返回2
        return 2;
    }
    else
    {
        //两个都带环,相遇点一定会在环内相遇,如果遇到则相交,遇不到则不相交
        SListNode* cur = meet1;
        while (cur->_next != meet1)
        {
            if (cur == meet2)
            {
                //相遇点在环内相遇,则两环相交
                 entry1 = SListNodeEntryNode(list1, meet1);
                 entry2 = SListNodeEntryNode(list2, meet2);

                if (entry1 == entry2)
                    return 3;//入口点相同,则环外相交
                else
                    return 4;//入口点不同,环内相交
            }

            cur = cur->_next;
        }

        //判断最后一个节点是否是相遇点
        if (cur == meet2)
        {
            //相遇点在环内相遇,则两环相交
            entry1 = SListNodeEntryNode(list1, meet1);
            entry2 = SListNodeEntryNode(list2, meet2);

            if (entry1 == entry2)
                return 3;//入口点相同,则环外相交
            else
                return 4;//入口点不同,环内相交

        }

        //如果meet1没有遇到meet2,则两个带环链表没有相交  
        return -1;

    }

}

获取相交点:

//获取相交点(可能带环)
SListNode* GetCrossNode1(SListNode* list1, SListNode* list2)
{
    assert(list1);
    assert(list2);

    size_t n = 0;
    SListNode* crossnode = NULL;//保存相交点

    n = SListIsCrossNode1(list1, list2);

    if (n == 1)
    {
        //两个都不带环相交,则转换为不带环的相交问题
        crossnode = GetCrossNode(list1, list2);
    }
    else if (n == 3)
    {
        //两个链表带环,并且环外相交,入口点相同
        //计算头到入口点的长度,让长的先走距离差,同时走

        size_t len1 = 0, len2 = 0;
        SListNode* cur1 = list1;
        SListNode* cur2 = list2;
        SListNode* meet1 = SListIsCircle(list1);//链表1的相遇点
        SListNode* entry = SListNodeEntryNode(list1, meet1);//环的入口点

        while (cur1 != entry)
        {
            //计算链表1到入口点的长度
            ++len1;
            cur1 = cur1->_next;
        }
        while (cur2 != entry)
        {
            //计算链表2到入口点的长度
            ++len2;
            cur2 = cur2->_next;
        }


        size_t num = abs(len1 - len2);
        SListNode* longlist = list1;
        SListNode* shortlist = list2;
        //选出两个链表当中头到入口点距离较长的一个
        if (len1 < len2)
        {
            longlist = list2;
            shortlist = list1;
        }
        //较长链表先走长度之差
        while (num--)
        {
            longlist = longlist->_next;
        }

        while (longlist != shortlist)
        {
            longlist = longlist->_next;
            shortlist = shortlist->_next;
        }

        if (longlist == shortlist)
        {
            crossnode = longlist;
        }

    }
    else
    {
        //如果环内相交,找不到入口点
        return NULL;
    }

    return crossnode;

}
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值