链表面试题2——带环问题

注:本篇博客中所使用单链表基本操作函数均可在《线性数据结构——链表》中找到,链接https://blog.csdn.net/lw13572259173/article/details/81281601

单链表实现约瑟夫环

约瑟夫环:已知n个人(以编号1,2,3…n分别表示)围坐在一张圆桌周围。从编号为k的人开始报数,数到m的那个人出列;他的下一个人又从1开始报数,数到m的那个人又出列;依此规律重复下去,直到圆桌周围的人全部出列。

pNode LinkListToJoseph(pList * pplist)//单链表实现约瑟夫环
{
    assert(pplist);
    int N = 2; //报数(m)
    //1.先将链表变成环
    LinkListFormRing(pplist);
    //2.编号为1的人开始报数,删除数到N的结点
    pList cur = *pplist;
    while (cur != cur->next)
    {
        //报数
        for (int i = 1; i < N; i++)
        {
            cur = cur->next;
        }
        //删除
        cur->data = cur->next->data;//把下一个的值赋给这一个
        pList del = cur->next;//保存下一个
        cur->next = cur->next->next;//指向下一个的下一个
        free(del);
        del = NULL;
    }
    //4.直到只剩下一个结点(结点自己指向自己),返回
    return cur;
}
void LinkListFormRing(pList * pplist)//单链表成环
{
    //让尾节点指向头结点
    assert(pplist);
    pList cur = *pplist;
    while (cur->next != NULL)
    {
        cur = cur->next;
    }
    cur->next = *pplist;
}

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

带环:双指针,一个指针一次走一步,一个指针一次走两步,若两个指针相遇,则返回相遇点,若快指针=NULL,则不带环

pList LinkListRing(pList plist) //判断链表是否带环
{
    assert(plist);
    pList str1 = plist;//快指针
    pList str2 = plist;//慢指针
    while (NULL != str1)
    {
        str1 = str1->next->next;
        str2 = str2->next;
        if (str1 == str2) //两个指针相遇
        {
            return str1;
        }
    }
    return NULL;
}

返回结点个数:相遇点,慢指针再相遇点不动,快指针走并计数,直到再次相遇时返回

int LinkListRingLength(pList plist, pList cur)//求链表环长度
{
    assert(plist && cur);
    pList str1 = cur->next;//cur 相遇点
    int count = 0;
    while (str1 != cur)
    {
        str1 = str1->next;
        count++;
    }
    return count + 1; //加上相遇点
}

入口点:一个指针在起始位置,一个指针在相遇点,两指针同时走,相遇时,即为环起始点

pList LinkListRingEntrance(pList plist, pList cur)//求链表环入口点
{
    pList str1 = plist;
    while (str1 != cur)//两指针没相遇
    {
        str1 = str1->next;
        cur = cur->next;
    }
    return str1;//两指针相遇返回
}

判断两个链表是否相交,若相交,求交点

这类问题,主要分为两种情况

1.链表不可能带环

链表不可能带环,有三种可能性
这里写图片描述

相交:两个链表均走到最后一个结点,看是否相等

int LinkListIntersect(pList plist1, pList plist2)//判断两个链表是否相交
{
    assert(plist1 && plist2);
    while (NULL != plist1->next) //找到最后一个结点
    {
        plist1 = plist1->next;
    }
    while (NULL != plist2->next) //找到最后一个结点
    {
        plist2 = plist2->next;
    }
    if (plist1 == plist2)
    {
        return 1;
    }
    return 0;
}

交点:求两个链表长度,长的走两个链表之差,再两个同时走

pList LinkListIntersection(pList plist1, pList plist2)//求两个链表交点
{
    assert(plist1 && plist2);
    int count1 = 0;
    int count2 = 0;
    pList cur1 = plist1;
    pList cur2 = plist2;
    while (NULL != cur1) //求链表1的长度
    {
        cur1 = cur1->next;
        count1++;
    }
    while (NULL != cur2) //求链表2的长度
    {
        cur2 = cur2->next;
        count2++;
    }
    cur1 = plist1;
    cur2 = plist2;
    if (count1 > count2)//长的走两链表个数之差
    {
        for (int i = 0; i < count1 - count2; i++)
        {
            cur1 = cur1->next;
        }
    }
    else
    {
        for (int i = 0; i < count2 - count1; i++)
        {
            cur2 = cur2->next;
        }
    }
    while (cur1 != cur2)//两个同时走
    {
        cur1 = cur1->next;
        cur2 = cur2->next;
    }
    return cur1;
}
2.链表可能带环

当链表带环时,有两种情况
这里写图片描述

相交、交点(两个链表可能带环)
先判断两个链表是否带环(一个带环一个不带环的情况不存在),带环,分别求两个带环链表的入口点,判断两个链表的入口点是否相同,若相同,则交点在环外,将两个链表改造为Y型求交点;若不相同,则一个指针在链表1的入口点不动,另外一个指针在链表2的入口点开始遍历链表,看是否能够遇到链表1的入口点

pList LinkListRingIntersect(pList plist1, pList plist2)
{
    //判断链表1,2是否带环
    pList cur1 = LinkListRing(plist1);//返回交点
    pList cur2 = LinkListRing(plist2);
    if (cur1 && cur2)//都带环
    {
        //求环入口点
        pList str1 = LinkListRingEntrance(plist1, cur1);//返回入口点
        pList str2 = LinkListRingEntrance(plist2, cur2);
        //判断入口点是否相等
        if (str1 == str2) //相等,相交,交点在环外,开头到入口点改造成Y形
        {
            str1->next = NULL;
            str2->next = NULL;
            //求两个链表交点
            return LinkListIntersection(plist1, plist2);
        }
        else //不相等,判断是否相交
        {
            //从入口点开始遍历链表1,看会不会遇到链表2的入口点
            pList str11 = str1->next;
            while (str11 != str1)
            {
                if (str11 == str2)
                {
                    return str2;//交点在环上
                }
                str11 = str11->next;
            }
            return NULL;
        }
    }
    else if (cur1 == NULL && cur1 == NULL)//不带环
    {
        if (LinkListIntersect(plist1, plist2))//判断两个链表是否相交
        {
            return LinkListIntersection(plist1, plist2);//交点
        }   
        return NULL;
    }
    else
    {
        return NULL;
    }
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值