码随想录算法训练营第4天 | Leetcode24 两两交换链表的节点, 19 删除链表的倒数第N个节点,面试题 02.07. 链表相交, 142.环形链表II

@代码随想录算法训练营第4天 | Leetcode24 两两交换链表的节点, 19 删除链表的倒数第N个节点,面试题 02.07. 链表相交, 142.环形链表II

24 两两交换链表的节点

视频链接:
https://programmercarl.com/0024.%E4%B8%A4%E4%B8%A4%E4%BA%A4%E6%8D%A2%E9%93%BE%E8%A1%A8%E4%B8%AD%E7%9A%84%E8%8A%82%E7%82%B9.html

第一遍读题思考(五分钟内,如果没有思路就写暴力解法思路,暴力解法思路也不清晰就写无

感觉难点还是在于遍历的时候如何判断cur指针在哪个位置,比如cur是在head节点的时候就需要创建一个tmp存储cur->next->next,然后让cur->next->next指向cur,cur->next指向tmp,然后使用虚拟头结点。

代码随想录解法思路

遇到链表问题不清楚的可以画图表示cur的位置,然后寻找while的终止条件以及指针交换的实际操作。

c++代码具体实现注意事项

注意创建dummyhead是要new一个新的ListNode。注意交换条件和while终止条件的判断。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* swapPairs(ListNode* head) {

        ListNode* dummyhead = new ListNode(0);
        dummyhead->next = head;
        ListNode* cur = dummyhead;
        while(cur->next!=nullptr && cur->next->next!=nullptr){
            ListNode* tmp = cur->next;
            ListNode* ttmp = cur->next->next->next;
            cur->next = cur->next->next;
            cur->next->next = tmp;
            cur->next->next->next = ttmp;
            cur = tmp;
        }
        return dummyhead->next;
    }
};

学习时长

30 分钟

19 删除链表的倒数第N个节点

https://programmercarl.com/0019.%E5%88%A0%E9%99%A4%E9%93%BE%E8%A1%A8%E7%9A%84%E5%80%92%E6%95%B0%E7%AC%ACN%E4%B8%AA%E8%8A%82%E7%82%B9.html

第一遍读题思考(五分钟内,如果没有思路就写暴力解法思路,暴力解法思路也不清晰就写无)

用一个while先把链表总长度遍历出来,然后与n求差值得到正着数的No.,然后再用dummyhead加cur的招式即可搞定,就是需要两个循环看起来不聪明的样子,看一下视频的解法。

代码随想录解法思路

在dummyhead的基础上结合快慢双指针的技巧,快指针比慢指针多走n+1步,当快指针指向nullptr的时候,慢指针刚好是要删除的node的前一个node。关于实现快指针比慢指针多走了n+1步这个操作还是借助了while循环实现的。

c++代码具体实现注意事项

还是要熟练dummyhead的初始化,时刻掌握指针位置。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode() : val(0), next(nullptr) {}
 *     ListNode(int x) : val(x), next(nullptr) {}
 *     ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution {
public:
    ListNode* removeNthFromEnd(ListNode* head, int n) {
        ListNode* dummyhead = new ListNode(0);
        dummyhead->next = head;
        ListNode* fast = dummyhead;
        ListNode* slow = dummyhead;
        while(n--){
            fast = fast->next;
        }
        while(fast->next != nullptr){
            fast = fast->next;
            slow = slow->next;
        }
        slow->next = slow->next->next;
        return dummyhead->next;
    }
};

学习时长

20分钟

面试题 02.07. 链表相交

https://programmercarl.com/%E9%9D%A2%E8%AF%95%E9%A2%9802.07.%E9%93%BE%E8%A1%A8%E7%9B%B8%E4%BA%A4.html

第一遍读题思考(五分钟内,如果没有思路就写暴力解法思路,暴力解法思路也不清晰就写无)

只能想到o(m*n的解法,就是循环套循环然后比较地址是否一致)。

代码随想录解法思路

先用两个循环分别遍历出A和B链表的长度,然后让两个链表尾尾对齐,开始遍历直到指针相同,因为后面都相同的部分一定是具有比min(len(A), len(B))还要小的长度。

c++代码具体实现注意事项

细节还是比较多的,还是要时刻注意curA和curB的位置。然后注意lenA比lenB小的情况下可以采用swap函数把lenA/lenB和curA/curB全部换位置,好让dummyA所代表的永远是最长的链表。详细的参考下面的代码。

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
        ListNode* dummyheadA = new ListNode(0);
        ListNode* dummyheadB = new ListNode(0);
        dummyheadA->next = headA;
        dummyheadB->next = headB;
        int lenA{0};
        int lenB{0};
        ListNode* curA = dummyheadA;
        ListNode* curB = dummyheadB;
        while(curA->next != nullptr){
            lenA++;
            curA = curA->next;
        }
        while(curB->next != nullptr){
            lenB++;
            curB = curB->next;
        }
        curA = dummyheadA->next;
        curB = dummyheadB->next;
        if(lenB > lenA){
            swap(lenA, lenB);
            swap(curA, curB);
        }
        int gap = lenA - lenB;
        while(gap--){
            curA = curA->next;
        }
        while(curA!=nullptr){
            if(curA == curB){
                return curA;
            }
            curA = curA->next;
            curB = curB->next;
        }
        return nullptr;
    }
};

学习时长

30 分钟

142.环形链表II

https://programmercarl.com/0142.%E7%8E%AF%E5%BD%A2%E9%93%BE%E8%A1%A8II.html

第一遍读题思考(五分钟内,如果没有思路就写暴力解法思路,暴力解法思路也不清晰就写无)

没想法。

代码随想录解法思路

很巧妙。创建快慢双指针,这次快慢双指针移动的条件很简单,每次快指针移动两个位置,慢指针移动一个位置即可。如果链表中有环,那么快慢指针必定相遇,不然的话快指针走到null。

  1. 为什么必定相遇?
    因为如果存在环的话,慢指针刚进入环,快指针已经在环里绕过n圈了,然后快慢指针同时绕环,根据前面两者的速度定义,站在慢指针的角度来看,相当于快指针每次走一步来向他接近,所以必然相遇。
  2. 相遇后能怎样?
    相遇之后,在相遇的位置设定一个新的指针index1,在原始head处设定一个新的指针index2,这两个指针再同时移动用步长1移动,等到两个指针相遇,相遇的地方就是环开始的地方。
    具体的查看下图的说明和公式推导。

在这里插入图片描述

c++代码具体实现注意事项

具体的代码实现还是有点东西需要注意的主要是赋值index1和index2应该都是什么,这里要明确x和z都是怎么定义的x是说走了几步从头到环起点,z是走了几步从fast和slow交点到了环起点,然后y是从环起点走了几步到了交点,所以index1就是slow和fast的交点,而index2应该是dummyhead而不是head。上图也可以模拟出index1和index2的位置,从ab相交位置开始推即可,途中ab就是真实的相交位置(根据fast走两步,slow走一步的问题)

/**
 * Definition for singly-linked list.
 * struct ListNode {
 *     int val;
 *     ListNode *next;
 *     ListNode(int x) : val(x), next(NULL) {}
 * };
 */
class Solution {
public:
    ListNode *detectCycle(ListNode *head) {
        ListNode* dummyhead = new ListNode(0);
        dummyhead->next = head;
        ListNode* fast = dummyhead;
        ListNode* slow = dummyhead;
        while(fast->next!=nullptr && fast->next->next!=nullptr){
            fast = fast->next->next;
            slow = slow->next;
            if(slow == fast){
                ListNode* index1 = slow;
                ListNode* index2 = dummyhead;
                while(index1!=index2){
                    index1 = index1->next;
                    index2 = index2->next;
                }
                return index1;
            }
        }
        return nullptr;
    }
};

学习时长

40分钟

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值