代码随想录算法训练营第四天 | 环形链表:快慢指针

24

讲解视频:帮你把链表细节学清楚! | LeetCode:24. 两两交换链表中的节点_哔哩哔哩_bilibili

文章讲解:代码随想录 (programmercarl.com)

状态:没看讲解,能直接写出(画图直接模拟)。故相关代码可通过LeetCode记录或“文章讲解”查看。这题我没用到虚拟头结点,建议用视频讲的虚拟头结点比较好。

面试高频题。

!!!!用虚拟节点,记住下面这个过程,按过程写代码即可。

!!!(1)(2)(3)的顺序确认:
对于图中第二行,由于p->next即节点1已经被q标记,所以p->next没什么用了,所以第一步就是p->next=r;节点2已经被r标记,若先操作r->next=q,那么节点3将与前几个节点失去联系,故要先操作q->next再操作r->next。
在这里插入图片描述
因为可能链表只包含一个节点或者是空节点,所以在循环遍历前只初始化第一个节点p,q和r到循环内再定义
注意while循环的条件。

class Solution {
public:
    ListNode* swapPairs(ListNode* head) {
        ListNode* _dummyNode = new ListNode();
        _dummyNode->next = head;
        ListNode* p = _dummyNode;
        
        while(p->next != nullptr && p->next->next != nullptr) {
            ListNode* q = p->next;
            ListNode* r = p->next->next;

            p->next = r;
            q->next = r->next;
            r->next = q;

            p = q;
        }

        return _dummyNode->next;
    }
};

19 快慢指针

讲解视频:链表遍历学清楚! | LeetCode:19.删除链表倒数第N个节点_哔哩哔哩_bilibili

文章讲解:代码随想录 (programmercarl.com)

快慢指针,好题目,值得记住。

状态:没看讲解,能直接写出(知道是双指针后突然想出,下次做不一定能想出,得复盘记住)。

细节tips(得记住,做删除结点题都有用):删除第i个结点时,要让指针指向第i-1个节点,故需要虚拟头结点_dummyHead。

思路:

  1. 让快指针fast、慢指针slow初始化都指向虚拟头结点_dummyHead,让快指针fast先移动n步,此时快慢指针距离为n,如下图所示。

    在这里插入图片描述

  1. 让快慢指针同时移动,直到快指针指向了最后一个节点,此时慢指针指向了待删节点的前一个节点,如下图。

在这里插入图片描述

代码如下

class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode* _dummyHead = new ListNode(); //虚拟头结点
        _dummyHead->next = head;

        ListNode *slow = _dummyHead;    //慢指针,初始化slow位置
        ListNode *fast = _dummyHead;    //快指针
        while(n--) {fast = fast->next;}     //初始化fast位置,思路第1步
        while(fast->next != nullptr){   //跳出循环时,slow指向待删节点的前一个节点。思路第2步
            slow = slow->next;
            fast = fast->next;
        }
        ListNode *tmp = slow->next;     //待删节点
        slow->next = tmp->next;
        delete tmp;
        return _dummyHead->next;
    }
};

面试题 02.07.

讲解视频:没有

文章讲解:代码随想录 (programmercarl.com)

状态:看了前面一部分讲解,能直接写出,估计自己想不出,当成不会做看待。

思路

  1. 求出两个链表的长度,并求出两个链表长度的差值x。
  2. 让长度较长的链表的头指针移动x步,移动后,两个头指针所处位置到最后一个节点的长度相等
  3. 同时移动两个头指针,若两个头指针的值相等,说明指向同一块内存,即相交;若移动完都没出现值相等的情况,说明不相交。

代码如下,

class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        //求链表A长度
        int lenA = 0;
        ListNode* cur = headA;
        while(cur != NULL){
            ++lenA;
            cur = cur->next;
        }
        //求链表B长度
        int lenB = 0;
        cur = headB;
        while(cur != NULL){
            ++lenB;
            cur = cur->next;
        }
        //若A比B长,先让A的头指针向后移(lenA-lenB)位
        if(lenA > lenB){
            int tmp = lenA - lenB;
            while(tmp--) headA = headA->next;
        }
        else{//否则,让B的头指针向后移(lenB-lenA)位
            int tmp = lenB - lenA;
            while(tmp--) headB = headB->next;
        }
        //此时headA与headB的链表长度一致。同时移动,若而二者地址相同,则相交
        while(headA != NULL){    //也可以headB!=NULL,二者肯定同时为NULL
            if(headA == headB) return headA;    //此时指向同一块内存,说明相交
            headA = headA->next;
            headB = headB->next;
        }
        return NULL;    //不相交
    }
};

142 环形链表:快慢指针

讲解视频:把环形链表讲清楚! 如何判断环形链表?如何找到环形链表的入口? LeetCode:142.环形链表II_哔哩哔哩_bilibili

文章讲解:代码随想录 (programmercarl.com)

状态:不会做

这题其实有两问:①链表是否有环?②找到环的入口。

Q1:如何判断链表是否有环?

思路:用双指针,快指针以快的速度在走,慢指针以慢的速度在走,若快慢指针相遇说明有环。若没环,快慢指针永远不会相遇,快指针会先到终点。

细节:快慢指针都从起点出发,快指针每次走2个节点,慢指针每次走1个节点。在环内,快指针相对于慢指针移动来说,每次就移动一个节点(相对速度为1),所以它俩一定会在环内相遇,而不会令快指针跳过慢指针。tips:若快指针每次走3个节点,慢指针走1个,就可能出现“快指针跳过慢指针”的情况。

Q2:如何找到环的入口?

思路:可通过对题目建立数学等式来解。快慢指针相遇时如下图所示,

在这里插入图片描述

从图中可知:在相遇时,慢指针slow走了x+y步,快指针fast走了x+n(y+z)+y步(n为快慢指针相遇前快指针在环内走了n圈,n>=1),以上为路程,而它们的速度分别为1和2,由于二者走了相同时间,

可建立等式(x+y)/1=[x+n(y+z)+y]/2,化简得x=n(y+z)-y,即x=(n-1)(y+z)+z。若n=1,则x=z,说明相遇点到入口处的距离z=起始点到入口处的距离x。当然,n也可以不取1,就表示index2从起始点到入口处的过程中,index1从相遇点开始走,走了n-1圈后,再走距离z,就可以在入口处与index2相遇(注意:index1和index2速度相同,都为1步)。—>这么写代码,不就可以找到入口处了嘛!

tips:!!慢指针在入环后,一定走了不到一圈就被快指针给追上了,原因可见讲解视频的12:25。

代码如下,

class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        ListNode *slow = head;//慢指针
        ListNode *fast = head;//快指针
        while(fast != NULL && fast->next != NULL){//!由于快指针走的比较快,故以它为判断条件;且快指针每次走两步,所以fast!=NULL判断第一步,fast->next!=NULL判断第二步。
            fast = fast->next->next;//快指针每次走两步
            slow = slow->next;//慢指针每次走一步

            if(fast == slow){//两个指针在相遇点相遇时
                ListNode *index1 = fast;//index1从相遇点开始走,这里也可以=slow
                ListNode *index2 = head;//index2从起始点开始走
                while(index1 != index2){//若有环,index1和index2一定会在入口处相遇
                    index1 = index1->next;
                    index2 = index2->next;
                }
                return index1;//相遇点,这里也可以是index2
            }
        }
        return NULL;
    }
};
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 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、付费专栏及课程。

余额充值