单链表判环的三种情况

本文详细探讨了单链表的环判断、环长度计算和环入口查找,以及在不同假设下两个链表是否相交及其交点求解方法。通过快慢指针策略,解决链表的各种复杂问题。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

链表的结构

typedef int DataType;

typedef struct ListNode
{
    DataType data;
    struct ListNode* next;
}ListNode;

一、链表带环的判断、若带环,求环的长度、环的入口

1)带环的判断

ListNode* Ring(ListNode *list)
{
    ListNode *Slow = list;
    ListNode *Fast = list;

    if (list == NULL || list->next == NULL)
    {
        return NULL;
    }

    Slow = Slow->next;
    Fast = Fast->next->next;
    while (Slow != Fast)
    {
        if (Fast->next == NULL || Fast->next->next == NULL)
        {
            return NULL;
        }

        Slow = Slow->next;
        Fast = Fast->next->next;
    }
    //返回相遇点
    return Slow;
}

思路:
通过快慢指针的移动来判断是否带环。慢指针每次+1,快指针每次+2。如果带环的话,快慢指针就一定会有相遇的一个节点,返回相遇的节点,为下面判断环的长度和入口做准备。如果快指针每次+3、+4……就不一定能够相遇,有可能存在刚好跳过相遇的点。

2)环的长度

int RingLength(ListNode *First)
{
    ListNode *Slow = First;
    ListNode *Fast = First;
    size_t count = 1;

    Fast = Fast->next->next;
    Slow = Slow->next;

    while (Fast != Slow)
    {
        count++;
        Fast = Fast->next->next;
        Slow = Slow->next;
    }

    return count;
}

思路:
让快指针和慢指针在环内从相遇点再走一圈。下一次相遇的时候,一定是慢指针走了一圈,快指针走了两圈。用一个计数记录下相遇的次数,这个就是环的长度。

3)环的入口

ListNode *RingEntry(ListNode *begin, ListNode *ring)
{
    if (begin == NULL || ring == NULL)
    {
        return NULL;
    }

    while (begin != ring)
    {
        begin = begin->next;
        ring = ring->next;
    }

    return begin;
}

思路:
begin指针从单链表的开头向前,ring指针从相遇点向前。两个指针每次+1,两个指针相遇的节点就是入口点。

这里写图片描述

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

ListNode *ListCross(ListNode *List1, ListNode *List2)
{

    ListNode *LongList = List1;
    ListNode *ShortList = List2;
    ListNode *Tmp = NULL;

    size_t long_length = 0;
    size_t short_length = 0;
    size_t tmp_length = 0;

    if (List1 == NULL || List2 == NULL)
    {
        return NULL;
    }

    //判断是不是相交
    while (List1)
    {
        ++long_length;
        List1 = List1->next;
    }

    while (List2)
    {
        ++short_length;
        List2 = List2->next;
    }

    if (List1 != List2)
    {
        return NULL;
    }

    //求交点
    if (short_length > long_length)
    {
        Tmp = LongList;
        LongList = ShortList;
        ShortList = Tmp;

        tmp_length = long_length;
        long_length = short_length;
        short_length = tmp_length;
    }
    tmp_length = long_length - short_length;

    while (tmp_length--)
    {
        LongList = LongList->next;
    }

    while (LongList != ShortList)
    {
        LongList = LongList->next;
        ShortList = ShortList->next;
    }
    return LongList;
}

思路:
1、两链表相交的模型:
这里写图片描述

2、判断是是否相交:
分别遍历两个链表,如果尾节点的地址相同,就证明两链表相交

3、求交点
两个链表分别遍历一次的时候同时记录下节点个数,将长链表的头指针先移动相差的节点数,再同时向前移动节点,两指针地址相同时,节点就是相交点。
这里写图片描述

三、判断两个链表是否相交,若相交,求交点。(假设至少一个链表带环)

ListNode *ListCrossRing(ListNode *List1, ListNode *List2)
{
    ListNode *MeetNode1 = Ring(List1);
    ListNode *MeetNode2 = Ring(List2);
    //默认长的是List1
    ListNode *LongList = List1;
    ListNode *ShortList = List2;

    size_t LongLenth = 0;
    size_t ShortLenth = 0;
    size_t Tmp = 0;

    //有一个链表不带环,不可能相交
    if (Ring(List1) == NULL || Ring(List2) == NULL)
    {
        return NULL;
    }

    //两个链表都带环

    //两链表不相交
    MeetNode1 = MeetNode1->next;

    while (MeetNode1 != MeetNode1 && MeetNode1 != MeetNode2)
    {
        MeetNode1 = MeetNode1->next;
    }

    if (MeetNode1 != MeetNode2)
    {
        return NULL;
    }

    //两链表相交
    //只有一个切入点
    MeetNode1 = Ring(List1);
    MeetNode2 = Ring(List2);
    while (List1 != MeetNode1)
    {
        List1 = List1->next;
        MeetNode1 = MeetNode1->next;
    }
    while (List2 != MeetNode2)
    {
        List2 = List2->next;
        MeetNode2 = MeetNode2->next;
    }

    //两链表不在环上相交,判断条件:两入口点一样
    if (MeetNode1 == MeetNode2)
    {
        while (LongList != MeetNode1)
        {
            LongList = LongList->next;
            LongLenth++;
        }

        while (ShortList != MeetNode2)
        {
            ShortList = ShortList->next;
            ShortLenth++;
        }

        if (LongLenth < ShortLenth)
        {
            Tmp = LongLenth;
            LongLenth = ShortLenth;
            ShortLenth = Tmp;

            LongList = List2;
            ShortList = List1;
        }

        Tmp = LongLenth - ShortLenth;

        while (Tmp--)
        {
            LongList = LongList->next;
        }

        while (LongList != ShortList)
        {
            LongList = LongList->next;
            ShortList = ShortList->next;
        }

        return LongList;
    }
    //两链表在环上相交,两入口点不一样,返回其中一个作为交点
    else
    {
        return MeetNode1;
    }
}

思路:
1、带环相交情况分析
这里写图片描述

2、情况一
对两个链表直接判环,有一个没有环,就符合情况一,没有相交

3、情况二
也是判环,用每个链表返回的相遇点比较,地址一样,表示是同一个环,否则,没有相交,不共用一个环

4、情况三
判断两者是不是在同一个环上面相交,可以用是不是具有同一个切入点判断。判断方法与上述第二题相同

5、情况四
if……else……剩下的就是这个情况了,即没有情况三的同一个切入点不满足

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值