c语言实现单链表面试题——进阶

c语言实现单链表面试题——进阶

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

解题:
快慢指针法:
快指针和慢指针从头结点开始走,快指针每次走两步,慢指针每次走一步,若不带环,快指针和慢指针不会相遇,若带环,快指针先进入环的入口点,当慢指针也进入环的入口点后,快慢指针将相遇。

这里写图片描述
1).判断单链表是否带环

ListNode *IsCycle(ListNode *list)//判断链表是否带环
{
    ListNode *fast=list;
    ListNode *slow=list;
    while(fast&&fast->next)
    {
        slow=slow->next;
        fast=fast->next->next;
        if(fast==slow)
        {
            return slow;
        }
    }
    return NULL;
}

2).若带环求环的长度

int GetCyclelen(ListNode* pList) //若带环求环的长度
{
    ListNode* fast = pList;
    ListNode* slow = pList;
    while (fast && fast->next)
    {
        fast = fast->next->next;
        slow = slow->next;
        if (fast == slow)
            break;
    }
    if (fast != slow || fast->next == NULL)
        return -1;
    else
    {
        int count = 0;
        do{
            fast = fast->next->next;
            slow = slow->next;
            count++;
        } while (fast != slow);
        return count;
    }
}

3).求环的入口点
思路:
1.同求环长,快慢指针相遇停止。
2.此时,将路程分为三段,一是 T——尾长(非环长度),二是 S——慢指针入环到被追上,三是 C——快指针追慢指针多跑的圈。那么,慢指针走的路程为 T+S,快指针走的路程为 T+S+C,可得 2(T+S)=T+S+C,T=C-S,此时,一个指针从起点出发走 T步,一个指针从快慢指针相遇点出发走 C-S 步,此时两指针刚好都在入口点。
3.两指针一个从头结点出发,一个从快慢指针相遇点出发,一步一步走,相遇的节点即为入口点(走的步数即为尾长 T(C-S))。

ListNode* GetCycleEnrty(ListNode* pList)
{
    ListNode* fast = pList;
    ListNode* slow = pList;
    while (fast && fast->next)
    {
        fast = fast->next->next;
        slow = slow->next;
        if (fast == slow)
            break;
    }
    if (fast != slow || fast->next == NULL)
        return NULL;
    while (pList != slow)
    {
        pList = pList->next;
        slow = slow->next;
    }
    return pList;
}

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

1.若无环链表相交,则尾节点相同(长这样 >— ),新建指针遍历两链表,并统计步数(求交点用)。
2.尾节点不同,返回 NULL。
3.比较两链表长度,快指针从长链表头结点开始,走比短链表长的节点数。
4.慢指针从另一链表头结点开始,与快指针一起一步一步走,指针相同时停止。返回任意一个指针

这里写图片描述

ListNode* intersect1(ListNode* pList1, ListNode* pList2)//链表不带环相交求交点
{
        int count1 = 0, count2 = 0;
        ListNode* p1 = pList1, *p2 = pList2;
        while (p1->next)
        {
            count1++;
            p1 = p1->next;
        }
        while (p2->next)
        {
            count2++;
            p2 = p2->next;
        }
        if (p1 != p2)return NULL;
        if (count1 >= count2)
        {
            count1 -= count2;
            while (count1--)
                pList1 = pList1->next;
            while (pList1 != pList2)
            {
                pList1 = pList1->next;
                pList2 = pList2->next;
            }
            return pList1;
        }
        else
        {
            count2 -= count1;
            while (count2--)
                pList2 = pList2->next;
            while (pList1 != pList2)
            {
                pList1 = pList1->next;
                pList2 = pList2->next;
            }
            return pList1;
        }
}

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

这里写图片描述

这里写图片描述
这里写图片描述

ListNode *IntersectRing(ListNode *list, ListNode *plist)
//判断两个链表是否相交,若相交,求交点。
//(假设链表可能带环)【升级版】
{
    ListNode* oep1 = OepRing(list);
    ListNode* oep2 = OepRing(plist);

    //两个链表都不带环(也就是上一题的解决方案)  
    if ((oep1 == NULL) && (oep2 == NULL))
    {
        return Intersect(list, plist);
    }

    //一个带环,一个不带环(不可能相交) 
    else if (((oep1 == NULL) && oep2) || ((oep2 == NULL) && oep1))
    {
        return NULL;
    }

    //两个都带环   
    else
    {
        //尾交(一个交点,一个入口点)
        //若两个链表的入口点一样则只有一个交点(交点也是入口点)
        //两链表各自环入口点已经求出,把list的环从入口点断开
        //可以转换成求不带环的相交链表求交点
        if (oep1 == oep2)
        {
            oep1->next = NULL;
            return Intersect(list, plist);//交点

        }

        else
        {
            //环交 
            //环交会有两个入口点,从一个入口点出发遍历环
            //遍历的过程中有结点等于另一个入口点,则表示环交,返回指向那个结点的指针(就是交点)
            //有两个交点,交点也是入口点。
            //否则,就是两个带环链表不相交


            //在这里返回的是第二个链表的入口点
            //也是第一个链表在第二个链表中的交点
            ListNode* cur = oep1->next;
            while (cur != oep1)
            {
                if (cur == oep2)
                {
                    return oep1;
                }
                cur = cur->next;
            }

            /*//在这里返回的是第一个链表的入口点
            //也是第二个链表在第一个链表中的交点

            ListNode* cur = oep2->next;
            while (cur != oep2)
            {
                if (cur == oep1)
                {
                    return oep2;
                }
                cur = cur->next;
            }*/
            return NULL;    //不相交  
        }
    }
}

4.复杂链表的复制。一个链表的每个节点,有一个指向next指针指向下一个节点,还有一个random指针指向这个链表中的一个随机节点或者NULL,现在要求实现复制这个链表,返回复制后的新链表。

//ps: 复杂链表的结构
struct ComplexNode
{
int _data ; // 数据
struct ComplexNode * _next; // 指向下一个节点的指针
struct ComplexNode * _random; // 指向随机节点(可以是链表中的任意节点 or 空)
};

分析:首先,复杂链表的复制,每个节点有两个指针next和指针random,复制的时候要将两个指针的信息全部复制上,因此我们可以一个指针一个指针依次复制。

具体思路:

1).创建新的节点,将其放在1和2的中间,如图所示:

这里写图片描述

void NewBackPrime(ComplexNode *list)
{
     ComplexNode *cur=list;
     while(cur)
     {
        ComplexNode *head=Init(cur->_data);
        head->_next=cur->_next;

        head->_random=NULL;
        cur->_next=head;
        cur=head->_next;
     }
}

2).复制随机节点(random)

这里写图片描述

void ComplexNodeRandom(ComplexNode *list)
//第二步,复制随机结点
{
    ComplexNode *cur = list;
    while (cur)
    {
        //找到插入的新结点
        ComplexNode *head = cur->_next;

        //让新结点指向的随机结点 
        //去指向 
        //原先结点指向随机结点的后一个
        if (cur->_random)
        {
            head->_random = cur->_random->_next;
        }
        cur = head->_next;

    }
}

3).新创建的节点链接起来,旧链表的节点链接起来,就完成了复杂链表的复制。

ComplexNode *RemoveNewCode(ComplexNode *list)
//第三步,让新创建链表的结点链接起来,原先链表的结点链接起来
{
    ComplexNode *cur = list;
    ComplexNode *head = NULL;
    ComplexNode *tmp = NULL;
    if (cur)
    {
        head = tmp = cur->_next;
        cur->_next = tmp->_next;
        cur = cur->_next;
    }
    while (cur)
    {
        //让新创建链表的结点链接起来
        tmp->_next = cur->_next;
        tmp = tmp->_next;

        //原先链表的结点链接起来
        cur->_next = tmp->_next;
        cur = cur->_next;
    }
    return head;

}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值