@代码随想录算法训练营第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。
- 为什么必定相遇?
因为如果存在环的话,慢指针刚进入环,快指针已经在环里绕过n圈了,然后快慢指针同时绕环,根据前面两者的速度定义,站在慢指针的角度来看,相当于快指针每次走一步来向他接近,所以必然相遇。 - 相遇后能怎样?
相遇之后,在相遇的位置设定一个新的指针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分钟