代码随想录训练营day04

第二章链表part02

1.LeetCode 两两交换链表中的节点

1.1题目链接:24.两两交换链表中的节点

文章讲解:代码随想录
视频讲解:B站卡哥视频讲解

1.2思路:两两节点进行交换,首先会想到奇数节点和偶数节点两种情况,在偶数情况下,直接可以把所有的节点进行交换完全,但是技术情况下,剩余的节点只能保留不变。因此节点交换的终止条件就是cur->next,cur->next->next不能位空指针。原理示意图如下:

初始时,cur指向虚拟头结点,然后进行如下三步:
在这里插入图片描述
操作之后,链表如下:
在这里插入图片描述
更直观的展示交换后情况如下:
在这里插入图片描述

1.3附加代码如下所示:

class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        ListNode*dummyhead=new ListNode(0);
        dummyhead->next=head;
        ListNode*cur=dummyhead;
        //理解循环截止条件是两种情况,一是偶数情况:cur->next,cur->next->next不能为空节点;二是奇数情况,生于一个节点无法进行交换保留下来不动
        while(cur->next!=NULL&&cur->next->next!=NULL)
        {
            //把要破环的节点提前保留下来,不然后面无法进行更改节点
            ListNode*temp=cur->next;
            ListNode*temp1=cur->next->next->next;

            //进行节点交换
            cur->next=cur->next->next;
            cur->next->next=temp;
            cur->next->next->next=temp1;

            //节点更新
            cur=cur->next->next;//向后移动两位进行下一轮交换
        }
        return dummyhead->next;
    }
       
};

2.LeetCode 删除链表的倒数第N个节点

2.1题目链接:19.删除链表的倒数第N个节点

文章讲解:代码随想录
视频讲解:B站卡哥视频讲解

2.2思路:要删除倒数第N个节点那么就找到该节点的前一个节点,采用双指针方法:倒数第N个节点删除,就让快慢指针初始时相差N个节点,然后再同时向后移动,但是这样的话遍历到终点时,慢指针指向的就是要删除的节点,无法得到该节点的前一个节点,所以为此我们需要初始时快慢指针相差N+1个节点,那么遍历到终点时慢指针就指向要删除结点的前一个节点了,可以进行删除操作了。

定义fast指针和slow指针,初始值为虚拟头结点,如图:
在这里插入图片描述
fast首先走n + 1步 ,为什么是n+1呢,因为只有这样同时移动的时候slow才能指向删除节点的上一个节点(方便做删除操作),如图:
在这里插入图片描述fast和slow同时移动,直到fast指向末尾,如题:

在这里插入图片描述删除slow指向的下一个节点,如图:
在这里插入图片描述

2.3附加代码如下所示:

//采用双指针法:倒数第N个节点,就让快=快慢指针相差N个节点然后一起向后移动,直至到空节点,那么慢指针指向的为要删除的节点,
//但是要删除一个节点必须要得到该节点的前一个结点,于是应该让快指针在此基础上多移动一个节点,快指针应该初始相差N+1个节点
class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
       ListNode*dummyhead=new ListNode(0);
       dummyhead->next=head;
       ListNode*slow=dummyhead;
       ListNode*fast=dummyhead;
       
       n++;//快指针与慢指针相差的节点数n+1
       
       while(n--&&fast!=NULL) //此处应该写fast!=NULL,不能写fast->next!=NULL,不然会跳过一些节点
       {
           fast=fast->next;
       }

       while(fast)
       {
           slow=slow->next;
           fast=fast->next;
       }
       //要删除的节点
       ListNode*temp=slow->next;
       //更新删除后的节点指向
       slow->next=slow->next->next;
       //释放删除后的节点空间
       delete temp;

       return dummyhead->next;

    }
};

3.LeetCode 环形链表II

3.1题目链接:142.环形链表II

文章讲解:代码随想录
视频讲解:B站卡哥视频讲解

3.2思路:环形链表首先是判断有没有环,然后才能去找环形的入口;采用快慢指针法。快指针一次走两个节点,慢指针一次走一个节点,这样如果有环形存在的话必有指针fast=slow,根据据推理公式如果有环形的话,相遇点必定满足X=Z条件,让另外两指针一个从起始点一个从相遇点出发知道二者指针相等则找到环形入口交点为当前指针。

fast是走两步,slow是走一步,其实相对于slow来说,fast是一个节点一个节点的靠近slow的,所以fast一定可以和slow重合。
在这里插入图片描述
假设从头结点到环形入口节点 的节点数为x。 环形入口节点到 fast指针与slow指针相遇节点 节点数为y。 从相遇节点 再到环形入口节点节点数为 z。 如图所示:
在这里插入图片描述
那么相遇时: slow指针走过的节点数为: x + y, fast指针走过的节点数:x + y + n (y + z),n为fast指针在环内走了n圈才遇到slow指针, (y+z)为 一圈内节点的个数A。

因为fast指针是一步走两个节点,slow指针一步走一个节点, 所以 fast指针走过的节点数 = slow指针走过的节点数 * 2:

(x + y) * 2 = x + y + n (y + z)

两边消掉一个(x+y): x + y = n (y + z)

因为要找环形的入口,那么要求的是x,因为x表示 头结点到 环形入口节点的的距离。

所以要求x ,将x单独放在左面:x = n (y + z) - y ,

再从n(y+z)中提出一个 (y+z)来,整理公式之后为如下公式:x = (n - 1) (y + z) + z 注意这里n一定是大于等于1的,因为 fast指针至少要多走一圈才能相遇slow指针。

这个公式说明什么呢?

先拿n为1的情况来举例,意味着fast指针在环形里转了一圈之后,就遇到了 slow指针了。

当 n为1的时候,公式就化解为 x = z,

这就意味着,从头结点出发一个指针,从相遇节点 也出发一个指针,这两个指针每次只走一个节点, 那么当这两个指针相遇的时候就是 环形入口的节点。

也就是在相遇节点处,定义一个指针index1,在头结点处定一个指针index2。

让index1和index2同时移动,每次移动一个节点, 那么他们相遇的地方就是 环形入口的节点。
在这里插入图片描述

3.3附加代码如下所示:

//采用双指针法:慢指针一次走一个节点,快指针一次走两个节点
class Solution
{
public:
    ListNode*detectCycle(ListNode*head)
    {
       //要考虑两点,一是环形是否一定存在,存在的话满足什么条件,存在的话快慢指针一定会再环里相遇
       //二是存在环形的话在什么地方相遇以及环形入口在哪,根据推到公式可知,快慢指针相遇 之后,要找到环形入口
       //满足起始点到入口距离等于相遇点到入口距离即X=Z
       if(head==NULL)return NULL;
       
       ListNode*slow=head;
       ListNode*fast=head;
       
       while(fast->next!=NULL&&fast->next->next!=NULL)//快慢指针移动截止条件
       {
           slow=slow->next;
           fast=fast->next->next;
           if(fast==slow)//快慢指针相遇点
           {
               ListNode*index1=head;
               ListNode*index2=fast;
               while(index1!=index2)
               {
                   index1=index1->next;
                   index2=index2->next;
               }
               return index1;
           }

       }
       return NULL;

    }
   
};

4.LeetCode面试题.链表相交

4.1题目链接:02.07.链表相交

文章讲解:代码随想录

4.2思路:首先理解题目意思,链表相交不是数值相等就满足条件,二是指针相等才满足条件;链表长度不一致时候需要根据二者的长度差值N让长度长的链表先移动差值N位,然后再让二者同时移动若遇到链表指针相等则找到满足条件的交点。

我们求出两个链表的长度,并求出两个链表长度的差值,然后让curA移动到,和curB 末尾对齐的位置,如图:
我们求出两个链表的长度,并求出两个链表长度的差值,然后让curA移动到,和curB 末尾对齐的位置,如图:
在这里插入图片描述
此时我们就可以比较curA和curB是否相同,如果不相同,同时向后移动curA和curB,如果遇到curA == curB,则找到交点。

4.3附加代码如下所示:

//思路:先求出两个链表的长度然后求出两个链表的长度差值N,再将长度长的链表向后移动N位之后,再让两个链表同时向后移动直到遇到两个链表指针相等,即找到相交点
class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        ListNode*curA=headA;
        ListNode*curB=headB;
        int lenA=0;//链表A长度
        int lenB=0;//链表B长度
        int sub=0;//两个链表长度差值
        while(curA)
        {
            curA=curA->next;
            lenA++;
        }
        while(curB)
        {
            curB=curB->next;
            lenB++;
        }
        //此时应该重新将headA和headB指向curA和curB的,因为在前面的遍历之后指针最终指向了NULL
        curA=headA;
        curB=headB;
        if(lenA>lenB)
        {
            sub=lenA-lenB;
            while(sub--&&curA!=NULL)
            {
                curA=curA->next;
            }
        }
        else
        {
            sub=lenB-lenA;
            while(sub--&&curB!=NULL)
            {
                curB=curB->next;
            }
        }
        while(curA)//再次遍历curA和curB直到二者指针相等
        {
            if(curA==curB)
            {
                return curA;
            }
            curA=curA->next;
            curB=curB->next;

        }

        return NULL;
        
        
    }
};
  • 14
    点赞
  • 23
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
代码随想录算法训练营是一个优质的学习和讨论平台,提供了丰富的算法训练内容和讨论交流机会。在训练营中,学员们可以通过观看视频讲解来学习算法知识,并根据讲解内容进行刷题练习。此外,训练营还提供了刷题建议,例如先看视频、了解自己所使用的编程语言、使用日志等方法来提高刷题效果和语言掌握程度。 训练营中的讨论内容非常丰富,涵盖了各种算法知识点和解题方法。例如,在第14天的训练营中,讲解了二叉树的理论基础、递归遍历、迭代遍历和统一遍历的内容。此外,在讨论中还分享了相关的博客文章和配图,帮助学员更好地理解和掌握二叉树的遍历方法。 训练营还提供了每日的讨论知识点,例如在第15天的讨论中,介绍了层序遍历的方法和使用队列来模拟一层一层遍历的效果。在第16天的讨论中,重点讨论了如何进行调试(debug)的方法,认为掌握调试技巧可以帮助学员更好地解决问题和写出正确的算法代码。 总之,代码随想录算法训练营是一个提供优质学习和讨论环境的平台,可以帮助学员系统地学习算法知识,并提供了丰富的讨论内容和刷题建议来提高算法编程能力。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* *2* *3* [代码随想录算法训练营每日精华](https://blog.csdn.net/weixin_38556197/article/details/128462133)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_2"}}] [.reference_item style="max-width: 100%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值