关于单链表的一些面试题-进阶篇(C语言实现)

关于单链表的一些面试题-进阶篇(C语言实现)

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

上述问题可以分解成:
(1)判断单链表是否带环
(2)求带环单链表的环的长度
(3)求带环单链表的环的入口点

下面对上述问题分别进行求解:

* 判断单链表是否带环
易知带环单链表的特性,所以我们在这里使用快慢指针的方法来判断单链表是否带环。
若带环,则快慢指针最终会在环内相遇。
而若不带环,则快慢指针始终无法相遇。
以此为原理,我们即可实现判断单链表是否带环的函数。

示意图:

这里写图片描述

具体代码实现:

ListNode* IsCycle(ListNode* pList)//判断链表是否带环
{
    ListNode* slow = pList;
    ListNode* fast = pList;

    while(fast && fast->next)
    {
        slow = slow->next;
        fast = fast->next->next;

        if(slow == fast)
            return slow;
    }
    return NULL;
}

思考:在代码实现中,我们给快指针的定义是fast->next->next,即快指针一次走两步。那么在此处,快指针为何不能一次走三步或者是走四步呢?


* 求带环单链表的环的长度
只要单链表带环,则快慢指针必定会在环内相遇。
我们可以通过IsCycle函数来求出这个相遇点,然后围绕着这个相遇点走一圈,并通过一个临时变量记录下这个次数,即可求出返回的长度

示意图:

这里写图片描述

具体代码实现:

int GetCycleLen(ListNode* pList)//求带环链表环的长度
{
    ListNode* meetNode = IsCycle(pList);
    ListNode* cur = meetNode->next;
    int count = 1;

    if(meetNode == NULL)
    {
        return 0;
    }
    else
    {
        while(meetNode != cur)
        {
            cur = cur->next;
            count++;
        }
        return count;
    }
}


* 求带环单链表的环的入口点

这里写图片描述

如图所示,设头结点至入口点的长度为L,入口点至相遇点的长度为X,环长为C
易知,在快慢指针相遇时,
快指针已走过的步数为:L+N*C+X(相遇之前快指针可能已经绕环多次)
慢指针已走过的步数为:L+X
又慢指针每走一步,快指针移动两步,即快指针移动的步数是慢指针的两倍
故:2(L+X) = L + N*C+X
  2L+2X = L + N*C+X
  L = N*C-X
因此,从相遇点到入口点的距离 = 头结点至入口点的距离(即 绿色线区域 = L)

由以上推理出的原理,有具体代码实现如下:

ListNode* GetCycleEntry(ListNode* pList)//求带环链表的环的入口点
{
    ListNode* start = pList;
    ListNode* ret = IsCycle(pList);

    if(start == NULL)
        return NULL;
    else
    {
        while(start != ret)
        {
            start = start->next;
            ret = ret->next;
        }
        return ret;
    }
}
2.判断两个链表是否相交,若相交,求交点。(假设链表不带环)

链表相交(不带环)有两种情况,如下图所示:

这里写图片描述

采取gap结点计算长短链表的方法,可以很好的求出两链表是否带环,且返回交点或者空指针。

具体实现代码如下所示:

ListNode* CheckIsMeet(ListNode* pList1, ListNode* pList2)//求两链表是否相交(链表不带环)
{
    ListNode* cur1 = pList1;
    ListNode* cur2 = pList2;

    ListNode* LongList = pList1;
    ListNode* ShortList = pList2;

    int gap = 0;
    int len1 = 0;
    int len2 = 0;

    while(cur1)
    {
        len1++;
        cur1 = cur1->next;
    }
    while(cur2)
    {
        len2++;
        cur2 = cur2->next;
    }

    if(len1<len2)
    {
        LongList = pList2;
        ShortList = pList1;
    }

    gap = abs(len1-len2);
    while(gap--)
    {
        LongList = LongList->next;
    }

    while(LongList)
    {
        if(LongList == ShortList)
            return ShortList;
        LongList = LongList->next;
        ShortList =ShortList->next;
    }
    return NULL;
}
3.判断两个链表是否相交,若相交,求交点。(假设链表可能带环)【升级版】

假设链表带环,则相交有以下几种情况,如图所示:
这里写图片描述


具体代码实现如下:

ListNode* CheckIsCross(ListNode* pList1, ListNode* pList2)//求两链表是否相交(链表可能带环)
{
    ListNode* enter1 = GetCycleEntry(pList1);
    ListNode* enter2 = GetCycleEntry(pList2);
    ListNode* cur  = enter1;


    //两链表都不带环
    if(IsCycle(pList1) == NULL && IsCycle(pList2) == NULL)
    {
        CheckIsMeet(pList1,pList2);
    }

    //两链表中有一个带环,一个不带环-——即两链表不可能相交
    else if(IsCycle(pList1) == NULL || IsCycle(pList2) == NULL)
    {
        return NULL;
    }

    else
    {
        //交点在环外
        //去环,转化为链表不带环的相交问题
        if(enter1 == enter2)
        {
            enter1->next = NULL;
            CheckIsMeet(pList1,pList2);
        }
        //交点在环内
        //遍历环,若找到另一个入口点,则返回其中一个入口点
        else
        {
            cur = enter1->next;
            if(cur != enter1)
            {
                if(cur == enter2)
                    return enter2;
                cur = cur->next;
            }
            return NULL;
        }
    }
}
4.复杂链表的复制。一个链表的每个节点,有一个指向next指针指向下一个节点,还有一个random指针指向这个链表中的一个随机节点或者NULL,现在要求实现复制这个链表,返回复制后的新链表。
  • 复杂链表的复制具体思路如下:

这里写图片描述

1、在原链表的每个节点后面插入一个新结点,新结点的data与之前的结点data相同
2、将原结点的random赋给新结点的random
3、取出新结点,链成新链表

  • 代码实现如下所示
ComplexNode* BuyNode(DataType x)//复杂链表的创建
{
    ComplexNode* ptr = (ComplexNode*)malloc(sizeof(ComplexNode));
    ptr->_data = x;
    ptr->_next = NULL;
    ptr->_random = NULL;
    return ptr;
}


void PrintComplexList(ComplexNode* cList)//打印复杂链表
{
    ComplexNode* cur = cList;
    while(cur)
    {
        printf("%d->",cur->_data);
        cur = cur->_next;
    }
    printf("NULL\n");
}


ComplexNode* CopyNodeList(ComplexNode* cList)//复杂链表的复制
{
    ComplexNode* cur = cList;
    ComplexNode* tmp = NULL;
    ComplexNode* newList = NULL;

    //链表中没有元素
    if(cList == NULL)
        return NULL;

    //链表中只有一个元素
    else if(cList->_next == NULL)
    {
        return BuyNode(cList->_data);
    }

    //链表中有多个元素
    else
    {
        //复制相同的元素到每个结点之后去
        while(cur)
        {
            tmp = BuyNode(cur->_data);
            tmp->_next = cur->_next;
            cur->_next = tmp;

            cur = cur->_next->_next;
        }

        while(cur)
        {
            //讨论random是否为空
            if(cur->_random == NULL)
                cur->_next->_random = NULL;
            else
            {
                cur->_next->_random = cur->_random->_next;
            }
            cur = cur->_next->_next;
        }

        //取出新链表
        newList = cList->_next;
        cur = cList->_next;
        while(cur->_next)
        {
            cur->_next = cur->_next->_next;
            cur = cur->_next;
        }
        cur->_next = NULL;
        return newList;
    }
}

void PushComplexList(ComplexNode** ccList, DataType x)//复杂链表的尾插法
{
    ComplexNode* tmp = NULL; 

    if((*ccList) == NULL)
        (*ccList) = BuyNode(x);

    else if((*ccList)->_next == NULL)
        (*ccList)->_next = BuyNode(x);

    else
    {
        tmp = *ccList;
        while(tmp->_next)
        {
            tmp = tmp->_next;
        }
        tmp->_next = BuyNode(x);
    }
}
  • 2
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值