【数据结构】链表面试题★★★★★

判断两个链表是否相交,若相交,求交点(假设链表不带环)

  • 遍历两个链表到最后一个结点,若两链表的最后一个结点地址相同则相交
  • 求得两个指针的长度,然后长链表先遍历(长链表-短链表)步,然后两链表同时向后遍历,当链表遍历到结点地址相同的时候,该结点为交点
pNode FindInterNode(pNode p1, pNode p2)
{
    pNode cur1 = p1;
    pNode cur2 = p2;
    int len1 = 0;
    int len2 = 0;
    int front = 0;

    while (cur1->next != NULL)
    {
        cur1 = cur1->next;
    }
    while (cur2->next != NULL)
    {
        cur2 = cur2->next;
    }

    if (cur1 == cur2)//条件成立则相交
    {
        cur1 = p1;
        cur2 = p2;
        len1 = GetListLength(cur1);
        len2 = GetListLength(cur2);

        if (len1 > len2)
        {
            front = len1 - len2;
            while (front--)
            {
                cur1 = cur1->next;
            }
        }
        else
        {
            front = len2 - len1;
            while (front--)
            {
                cur2 = cur2->next;
            }
        }

        while (cur1 != cur2)
        {
            cur1 = cur1->next;
            cur2 = cur2->next;
        }
        return cur1;

    }
    else
    {
        return NULL;
    }
}

判断单链表是否带环?若带环,求环的长度?求环的入口点

  • 借助快慢指针:当慢指针进入到环中,若快指针追上快指针(快慢指针相交)则说明带环,若快指针往后走的过程中遇到空指针域,则说明不带环
//如果有环,返回两指针在环内的相遇结点,无环返回空,后面的题目会用到此结点位置
pNode IsCircleLinkList(pNode plist)
{
    pNode fast = plist;//快指针一次走一步
    pNode low = plist;//慢指针一次走两步

    while (fast != NULL)
    {
        low = low->next;

        if (fast->next == NULL)
        {
            break;
        }
        fast = fast->next;

        if (fast->next == NULL)
        {
            break;
        }
        fast = fast->next;
        if (fast == low)
        {
            return low;
        }
    }
    return NULL;
}
  • 快慢指针相交时,再遍历一次链表,当回到交点位置,所遍历的长度即是环的长度
int LengthOfCircle(pNode plist)
{
    int count = 0;
    pNode cur = plist;
    pNode NodeInCircle = NULL;

    NodeInCircle = IsCircleLinkList(plist);
    //从头指针遍历到相遇结点位置
    while (cur != NodeInCircle)
    {
        cur = cur->next;
    }
    //计数时判断结点指针域,循环结束时count = 环的长度 - 1
    while (cur->next != NodeInCircle)
    {
        count++;
        cur = cur->next;
    }
    count++;
    return count;
}
  • (1)从结点处断开链表(将结点的next指针域指向空),然后将此问题转换为求两相交链表交点的问题。这种方法会将该链表破坏,如果后面还要使用该链表则会带来问题
pNode EntranceOfCircle(pNode plist)
{
    pNode ret = NULL;
    pNode cur1 = plist;
    pNode cur2 = NULL;
    pNode NodeInCircle = NULL;

    NodeInCircle = IsCircleLinkList(plist);

    cur2 = NodeInCircle->next;
    NodeInCircle->next = NULL;//找到环内相遇结点,断开环
    ret = FindInterNode(cur1, cur2);

    return ret;
}
  • (2)两个指针同时向后遍历,一个指针从环内交点开始,一个指针从头开始,最后肯定会在环的入口点相遇
pNode EntranceOfCircle_OP(pNode plist)
{
    pNode cur1 = plist;
    pNode cur2 = IsCircleLinkList(plist);

    while (cur1 != cur2)
    {
        cur1 = cur1->next;
        cur2 = cur2->next;
    }
    return cur1;
}

这里写图片描述
这里写图片描述

复制复杂链表

通过一般链表的复制方法,将新旧链表分开,无法复制出新链表的random指针指向,因为如果再通过一次同步循环遍历,会将新链表的random指针指向老链表random指针所指向的结点

新旧链表串成一条链表,此时便可以通过老链表的random找到新链表的random,复制完成后,将两条链表分开

构造复杂链表

typedef struct CNode
{
    DataType data;
    struct CNode *next;
    struct CNode *random;
}CNode, *pCNode;

pCNode BuyCNode(DataType data)
{
    pCNode newNode = (CNode *)malloc(sizeof(CNode));
    assert(newNode);

    newNode->data = data;
    newNode->next = NULL;
    newNode->random = NULL;

    return newNode;
}

void CNTest()
{
    pCNode plist = NULL;
    pCNode ret = NULL;

    pCNode plist1 = BuyCNode(1);
    pCNode plist2 = BuyCNode(2);
    pCNode plist3 = BuyCNode(3);
    pCNode plist4 = BuyCNode(4);

    plist = plist1;
    plist1->next = plist2;
    plist2->next = plist3;
    plist3->next = plist4;

    plist1->random = plist3;
    plist2->random = plist1;
    plist3->random = plist3;
    ret = CopyComplexLinkList(plist);
}

这里写图片描述

pCNode CopyComplexLinkList(pCNode plist)
{
    pCNode ret = NULL;
    pCNode cur = plist;
    pCNode newNode = NULL;
    pCNode next = NULL;
    pCNode newNext = NULL;
    //新旧链表串一起
    while (cur != NULL)
    {       
        newNode = BuyCNode(cur->data);
        newNode->next = cur->next;
        cur->next = newNode;

        cur = newNode->next;    
    }
    //然后复制random
    cur = plist;
    while (cur != NULL)
    {
        newNode = cur->next;
        if (cur->random != NULL)
        {
            newNode->random = cur->random->next;
        }
        cur = cur->next->next;
    }
    //新旧链表分开来
    cur = plist;
    ret = plist->next;//记录新链表起始位置

    while (cur != NULL)
    {
        newNode = cur->next;
        next = newNode->next;
        if (next == NULL)
        {
            newNext = NULL;
        }
        else
        {
            newNext = next->next;
        }
        cur->next = next;
        newNode->next = newNext;

        cur = next;
    }
    //最后返回新链表
    return ret;
}
  • (1)新旧链表串一起
  • (2)然后复制random
    这里写图片描述
  • (3)新旧链表分开来
    这里写图片描述
    这里写图片描述
  • (4)最后返回新链表

判断两个链表是否相交,若相交,求交点(链表可能带环)

这里写图片描述

根据上面的分析,两链表出现的状态一共有下面6种情况,画图表示

这里写图片描述

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值