C语言实现单链表面试题---进阶

单链表相关的博客:
C语言实现 单向链表
https://blog.csdn.net/qq_37941471/article/details/72961495
C语言实现单链表面试题—基础篇
https://blog.csdn.net/qq_37941471/article/details/78033970
C语言单链表的实现及其面试题—完整代码
https://blog.csdn.net/qq_37941471/article/details/80458397

单链表带环问题

  • 解决方法:快慢指针
    1.判断单链表是否带环?若带环,求环的入口点?若带环,求环的长度?
    2.判断两个链表是否相交,若相交,求交点。(假设链表不带环)
    3.判断两个链表是否相交,若相交,求交点。(假设链表可能带环)【升级版】

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

  • 如何求环的入口点:

这里写图片描述

  • 如何求环的长度:

思想:从相遇点开始遍历链表,一直走到和相遇点相等的地方,便是一个环的长度。

代码实现:

ListNode* IsCycle(ListNode* plist)//判断链表是否带环?
{
    ListNode* slow = plist;
    ListNode* fast = plist;
    if( plist == NULL || plist->next == NULL )
            return NULL;
    while( fast && fast->next )//如果不带环,快指针先走到尾(终止条件)
    {
        fast = fast->next->next;
        slow = slow->next;
        if( slow == fast )//如果带环,快慢指针一定会相遇
            return slow;
    }
    return NULL;
}

ListNode* GetEntry(ListNode* plist, ListNode* MeetNode)//若带环,求其入口点。
{
    assert( plist&&MeetNode);
    while( plist != MeetNode )//plist == MeetNode找到入口点
    {
        plist = plist->next ;
        MeetNode = MeetNode->next ;
    }
    return plist;
}

int GetCycle_Length(ListNode* MeetNode)//若带环,求环的长度
{
    ListNode* cur = MeetNode->next ;
    int num = 1;
    while( cur != MeetNode )
    {
        ++num;
        cur = cur->next;
    }
    return num;
}
void test()
{
    ListNode* list = NULL;
    ListNode* tail;
    ListNode* entry;//入口点
    ListNode* meet;//相遇点
    PushBack(&list, 1);
    PushBack(&list, 2);
    PushBack(&list, 3);
    PushBack(&list, 4);
    PushBack(&list, 5);
    PushBack(&list, 6);
    PrintList(list);

    tail = Find(list, 6);
    enrty = Find(list, 4);
    tail->next = entry;//带环

    meet = IsCycle(list);
    printf("入口点:%d\n", GetEntry(list, meet)->data);

    length = GetCycle_Length(meet);
    printf("环的长度为%d\n",length);
}

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

  • 判断两个链表是否相交?:

这里写图片描述

  • 若相交,求交点:

image

代码实现:

int IsCross_NoCycle(ListNode* plist1,ListNode* plist2)//12.判断两个链表是否相交?(假设链表不带环)
{
    assert(plist1&&plist2);
    if( plist1 == NULL || plist2 == NULL )
        return -1;
    while(plist1->next)
    {
        plist1 = plist1->next ;
    }
    while(plist2->next)
    {
        plist2 = plist2->next;
    }
    if( plist1 == plist2 )
        return 1;
}
ListNode* GetCrossNode(ListNode* plist1, ListNode* plist2)//若相交,求交点
{
    ListNode* longlist = plist1,*cur1 = plist1;
    ListNode* shortlist = plist2,*cur2 = plist2;
    int len1 = 0,len2 = 0;
    int gap;
    //求两个链表的长度,判断哪个长?
    while(cur1)
    {
        len1++;
        cur1 = cur1->next;
    }
    while(cur2)
    {
        len2++;
        cur2 = cur2->next;
    }
    if( len1<len2 )
    {
        longlist = plist2;
        shortlist = plist1;
    }
    gap = abs(len1-len2);
    //长的先走gap步,gap--(gap步),--gap(gap-1步)
    while(gap--)
    {
        longlist = longlist->next ;
    }
    //一起走,相遇点就是交点
    while( longlist != shortlist )
    {
        longlist = longlist->next ;
        shortlist = shortlist->next ;
    }
    return shortlist;
}


void test()
{
    ListNode* cross, *tail;
    ListNode* list1 = NULL;
    ListNode* list2 = NULL;

    PushBack(&list1, 1);
    PushBack(&list1, 2);
    PushBack(&list1, 3);
    PrintList(list1);

    PushBack(&list2, 4);
    PushBack(&list2, 5);
    PushBack(&list2, 6);
    PushBack(&list2, 7);
    PushBack(&list2, 8);
    PushBack(&list2, 9);
    PrintList(list2);

    cross = Find(list2, 8);
    tail = Find(list1, 3);
    tail->next = cross;

    //判断两个链表是否相交?
    if( 1 == IsCross_NoCycle(list1,list2) )
        printf("两个链表相交,");
    //求交点
    printf("交点是:%d\n", GetCrossNode(list1, list2)->data);
}

3.判断两个链表是否相交,若相交,求交点。(假设链表可能带环)【升级版】

这里写图片描述

代码实现:

ListNode* IsCross_Cycle(ListNode* plist1,ListNode* plist2)
{
    //求链表带环的相遇点
    ListNode* MeetNode1 = IsCycle(plist1) ;
    ListNode* MeetNode2 = IsCycle(plist2) ;
    ListNode* entry1 ;
    ListNode* entry2 ;
    ListNode* meet,*cur1;
    //判断链表是否带环
    if( MeetNode1 == NULL || MeetNode2 == NULL )
        return NULL;//其中一个链表不带环,则不会相交,返回NULL
    //两个链表都带环,相交的两种情况
    //1.相同的入口点
    //2.不同的入口点
    entry1 = GetEntry(plist1,MeetNode1);
    entry2 = GetEntry(plist2,MeetNode2);
    if( entry1 == entry2 )//相同的入口点,返回交点
    {
        entry1->next = NULL;
        entry2->next = NULL;
        meet = GetCrossNode(plist1,plist2);
        return meet;
    }
    //不同的入口点的情况(判断条件:从MeetNode1开始遍历链表,如果找到和MeetNode2相等的节点,则证明共用环)
    //如果相交,返回任意一个入口点
    cur1 = MeetNode1->next ;
    while( cur1 != MeetNode1 && cur1 != MeetNode2 )
    {
        cur1 = cur1->next ;
    }
    if( cur1 == MeetNode2 )
        return entry1;
    return NULL;//不相交
}
void test()//判断两个链表是否相交?相同的入口点
{
    ListNode* cross, *tail1,*tail2;
    ListNode* entry;
    ListNode* list1 = NULL;
    ListNode* list2 = NULL;
    ListNode* meet;

    PushBack(&list1, 1);
    PushBack(&list1, 2);
    PushBack(&list1, 3);
    PrintList(list1);

    PushBack(&list2, 4);
    PushBack(&list2, 5);
    PushBack(&list2, 6);
    PushBack(&list2, 7);
    PushBack(&list2, 8);
    PushBack(&list2, 9);
    PushBack(&list2, 10);
    PushBack(&list2, 11);
    PrintList(list2);

    cross = Find(list2, 8);//交点
    tail1 = Find(list1, 3);
    tail1->next = cross;

    tail2 = Find(list2, 11);
    entry = Find(list2, 9);//入口点
    tail2->next = entry;//带环
    meet = IsCross_Cycle(list1,list2);
    printf("交点是:%d\n",meet->data);
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值