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

来源:互联网 发布:0耗材空气净化器 知乎 编辑:程序博客网 时间:2024/06/02 21:27

关于单链表的一些面试题-进阶篇(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);    }}
原创粉丝点击