单链表的OJ题目(内含思路)

1.找到两个链表的公共节点

题目路径:160. 相交链表 - 力扣(LeetCode)

题目的内容:

本题的思路:首先先判断,两个链表是否相交,接着再求取两个链表的公共节点

1.1判断链表是否相交

LN*A=headA;
    int counta=0;
    LN*B=headB;
    int countb=0;
    while(A->next!=NULL)
    {
        A=A->next;
        counta++;
    }
    while(B->next!=NULL)
    {
        B=B->next;
        countb++;
    }
    if(A!=B)
    return NULL;

只要判断两个尾节点是否相等,如果相等就说明两个链表是有公共点的,没有就说明不相交,直接返回NULL。

1.2找到并返回公共点

//2.计算相交节点为位置并返回
    //两个链表的长度的,分为两种情况
    if(counta>countb)
    {
        int gap=counta-countb;
        LN*AA=headA;
        LN*BB=headB;
        while(gap--)
        {
            AA=AA->next;
        }
        while(AA!=BB)
        {
            AA=AA->next;
            BB=BB->next;
        }
        return AA;

    }
    else
    {
        int gap=countb-counta;
        LN*AA=headA;
        LN*BB=headB;
        while(gap--)
        {
            BB=BB->next;
        }
        while(AA!=BB)
        {
            AA=AA->next;
            BB=BB->next;
        }
        return BB;

    }

分别求出两个链表的长度,在第一步就可以同步完成。接着判断两个链表的长度(下面将两个链表称为A链表和B链表),下面讨论A链表的长度大于B链表的情况(实际上B大于A的情况一样)。是指向A的指针先走 A链表的长度-B链表的长度,之后指向A链表和B链表的指针同时向后面走,直到AA指针和BB指针相等,相等就说明知道了第一个公共节点。

 2.随机链表的深拷贝

题目路径:138. 随机链表的复制 - 力扣(LeetCode)

题目内容:

本题思路:本题虽然考的是链表的复制,但实际上的难点是random指针指向的内容的拷贝,就C语言而言,如果只是将新链表直接复制原链表,时间复杂度过高。需要将新链表和原链表构成关系,在构成一定的关系的情况下,对于random指针就行一个拷贝。

2.1将每个新的节点放在原链表的节点之后

if(head==NULL)
    return NULL;
    SN*pucr=head;
    //1.每次拷贝的节点放在原链表的节点之后
    while(pucr)
    {
        SN*newnode=(SN*)malloc(sizeof(SN));
        newnode->val=pucr->val;
        newnode->next=pucr->next;
        pucr->next=newnode;
        pucr=newnode->next;
    }

首先先判断原链表是否是空链表,如果是空链表,新链表没必要复制,直接返回NULL。接着创建pucr指针遍历原链表,遍历每一个节点后,在该节点之后插入一个新的节点,这个新节点的val和next指针和前一个节点相同。 

2.2给新链表的random节点

//2.通过前面的形式,得出新添加节点的random
    SN*ppucr=head;
    while(ppucr)
    {
        if(ppucr->random==NULL)
        ppucr->next->random=NULL;
        else
        ppucr->next->random=ppucr->random->next;

        ppucr=ppucr->next->next;
    }

 通过我们上面的操作,使得原链表和新链表之间存在关系,使用ppucr指针进行一个遍历,如果在原链表中,遍历节点的random指针指向为空,那么它的下一个指针的random指针也指向为空(实际上就是该节点在新链表的对应节点为空);如果遍历节点的random指针不指向空,那么该遍历节点的下一个节点的random所指向的节点就是该遍历节点的random指针所指向的节点的下一个节点(这是本题最重要的代码,如不能理解,建议画图理解),之所以能这样的,是前面我们将原链表和新链表构成关系。

2.3将新链表将从原链表剥离

SN*newhead=head->next;
    SN*ptrcop=newhead;
    SN*ptr=head->next->next;
    while(ptr)
    {
        ptrcop->next=ptr->next;
        ptrcop=ptrcop->next;
        ptr=ptr->next->next;

    }
    return newhead;

创建头指针指向新链表的头节点,通过遍历原链表,将刚刚插入的新链表的节点,尾插到 创建的头指针后面,这样就完成了对于原链表的深拷贝。

3.环形链表

题目路径:141. 环形链表 - 力扣(LeetCode)

题目内容:

本题思路:使用快慢指针,让快指针和慢指针同时动,如果说快指针指向NULL或者快指针的下一个指针指向为NULL,就说明单链表没有环:否则说明单链表是有环的,当慢指针进入环,并且慢指针等于快指针的时候结束循环。

3.1判断循环

//使用快慢指针
    //快指针能够追上慢指针说明有环
    //快指针没有追上慢指针说明没有环 
    ListNode*fast=head;
    ListNode*slow=head;
    while(fast&&fast->next)
    {
        //fast每一次走两步
        fast=fast->next->next;

        //slow每一次走一步
        slow=slow->next;

        if(slow==fast)
        return true;
    }

    //快指针走到了结尾,说明没有环
    return false;

fast指针是会比slow指针快的,如果fast是会指向NULL的或者fast的下一个节点为NULL(这个是有单链表节点的奇数或者偶数决定的):如果是单链表中存在环会进入死循环,跳出循环的条件是fast指针追上slow指针,fast指针的每次向后面找两个节点,slow指针每次向后面找一个节点。由于是死循环,slow指针是一定会进入循环的,fast指针和slow指针的速度相差一(这里的速度就是向后找几个节点),在这个环中就存在一个追击问题,速度差为一,fast最终是会等于slow指针的。

 4.环形链表(找入环的第一个节点)

题目路径:142. 环形链表 II - 力扣(LeetCode) 

题目内容:

本题思路:第一步判断单链表是否含有环,第二步求出单链表的的入环节点。 

4.1判断单链表是否含有环 

//1.先判断是否带环

    //快指针能够追上慢指针说明有环
    //快指针没有追上慢指针说明没有环 
    ListNode*fast=head;
    ListNode*slow=head;
    while(fast&&fast->next)
    {
        //fast每一次走两步
        fast=fast->next->next;

        //slow每一次走一步
        slow=slow->next;

        if(slow==fast)
        {
        //2.带环,确定带环的第一个节点
        ListNode*cur=head;
        ListNode*meet=slow;
        while(meet!=cur)
    {
        //fast 和 head 指针分别向后走一步
        meet=meet->next;
        cur=cur->next;
    }

        return meet;
        }
    }

    //快指针走到了结尾,说明没有环
    return NULL;

判断是否带环,其实步骤和上面的第三题一样,只是while循环结束的条件不止是fast==slow,而是在该条件基础上还有一些操作。 

4.2找到入环节点

代码其实就是上面代码if(slow==fast)之中的内容。

这个公式需要一定的推理:

 我们快指针的速度是慢指针的两倍,我们入环前的节点长度为X,环的长度为C,由于fast的速度是比慢指针快的,当慢指针刚进入环的时候,快指针一定是在慢指针之前的,也就是说快指针由于要追击慢指针,一定是会走至少一圈的环(也有可能环的长度很小,快指针在环里面走过了好几圈),我们不防设快指针走了N圈。为表示方便slow指针的速度是1,fast指针的速度是2(这里的1和2实际上就是快慢指针每次跳过的字节数)。

 第一个公式:2*X+2t=NC+X+t;

这个公式最终化简的关系是:

X=NC-t;

第二个公式:L=C-t;(L是slow和fast指针相遇的节点和入环节点之间的节点差)

仔细观察两公式,可将第一个公式化简为:

X=(N-1)C+C-t;

X比L大了(N-1)C,也就是说大了N-1圈个环的节点数。

现在重新初始化两个节点,一个节点cur初始化为这个单链表的首节点,另一个节点meet初始化为slow和fast指针相遇的节点,两个节点最终一定是会相遇的,而且相遇的节点就是入环节点。当N大于等于1时,meet就是环里面绕圈,最终是会和cur相遇的。

下面是我自己的画图,图画的有些丑,见谅

 

总结:

  本文介绍了几道关于单链表的算法题,如果有错讲误讲的内容,欢迎在评论区或者私信中纠正。当然,如果有更好的思路,也欢迎多多交流,让我见识和学习大佬的代码。 

  • 23
    点赞
  • 17
    收藏
    觉得还不错? 一键收藏
  • 7
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值