24. 两两交换链表中的节点
题目链接/文章讲解/视频讲解: 代码随想录
/**
* 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* dummy = new ListNode(-1);
dummy->next = head;
ListNode* prevNode = dummy;
while ((head != nullptr) && (head->next != nullptr)){
ListNode* firstNode = head;
ListNode* secondNode = head->next;
firstNode->next = secondNode->next;
secondNode->next = firstNode;
prevNode->next = secondNode;
prevNode = firstNode;
head = firstNode->next;
}
ListNode* result = dummy->next;
delete dummy;
return result;
}
};
做法和随想录有点不一样 用了 prev, 少用一个temp。
又是容易绕进去的一题,一次对了也还不能保证下次也对,三刷预定了
19.删除链表的倒数第N个节点
题目链接/文章讲解/视频讲解:代码随想录
/**
* 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* dummy = new ListNode();
ListNode* curr = head;
ListNode* fast = head;
dummy->next = head;
ListNode* prev = dummy;
for (int i = 0; i < n; i++){
fast = fast -> next;
}
while (fast != nullptr) {
prev = prev -> next;
curr = curr -> next;
fast = fast -> next;
}
prev->next = curr->next;
delete curr;
head = dummy->next;
delete dummy;
return head;
}
};
return 的时候忘记了重置head为dummy->next, 导致报了个heap-use-after-free on address的错,大概是删除了head但又试图return head导致的。
判断fast提前走几步时脑子转不动,所以举一个只有一个node的例子来决定走几步
面试题 02.07. 链表相交
本题没有视频讲解,大家注意 数值相同,不代表指针相同。
题目链接/文章讲解:代码随想录
同时似乎也是leetcode 160
忘记怎么解了,看到题的思路是先找终止点沿路count,然后决定谁先走几步,一一对比。但印象中好像有更好的解法,决定先看文章讲解。
检索了一番 没有一遍过的解法,除开一个一路走一路加标记的解法,但这改变了链表结构。
two pointer的解法是我印象中更加巧妙的一个,但实际上也需要走两遍,所以这次还是练习代码随想录里的count解法,虽然行数多些,但有一种朴实暴力的美。
/**
* 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) {
int count = 0;
ListNode *currA = headA;
ListNode* currB = headB;
while (currA != nullptr && currB!= nullptr){
currA = currA->next;
currB = currB->next;
}
while (currA != nullptr){
currA = currA->next;
count++;
}
while (currB!= nullptr){
currB = currB->next;
count--;
}
currA = headA;
currB = headB;
while (count > 0){
currA = currA->next;
count--;
}
while (count < 0){
currB = currB->next;
count++;
}
while (currA != nullptr && currB!= nullptr){
if (currA == currB){
return currA;
}
currA = currA->next;
currB = currB->next;
}
return NULL;
}
};
没意识到可以用自带的swap来省两行,但看上去还怪对称的 先不管了。下一刷应该还是会用two pointer, 因为写起来能短一点。
142.环形链表II
算是链表比较有难度的题目,需要多花点时间理解 确定环和找环入口,建议先看视频。
题目链接/文章讲解/视频讲解:代码随想录
这个快慢指针印象很深刻,但只记得怎么找环,记不清楚怎么找环入口了。
面试要是也这样就先用哈希表吧。
挣扎一下式子总结理清思绪:
慢指针步数(x+y), x为起点到环入口,y为环里距离
快指针步数 2(x+y),因为快指针快一倍
2 (x + y) - ab = x + y (快慢指针相交时,快的多走了a圈,一圈为b)
那么x+y = ab(一般我就卡在这里,但这样就肯定不行,需要再分解现状)
b拆成(y+z),入口到相遇点的距离和剩余距离组成一圈
那么x+y = a(y+z)
代码随想录进行了因式分解 x = (a-1)(y + z) +z
y + z 是一圈的距离
这是我第二个容易卡壳的地方,之前总是想不清楚 这个式子的(a-1)如何能对上号。通过分析我意识到 可以举出例子:一个指针在快慢交点,一个指针在起点开始等速走的时候,什么时候会有很多圈? 那应当就是 环小,x很长的时候。这个时候环内指针要多走很多圈才可以和起点开始的指针相遇,那就是(a-1)圈。
所以如果一个指针在交点,一个指针在起点开始等速走,交点指针走 z + (a - 1) 圈 步 等于 起点指针走x步 而环入口的定义便是 起点开始的第x个node。
/**
* 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* slow = head;
ListNode* fast = head;
while (fast != NULL && fast->next != NULL){
fast = fast->next->next;
slow = slow->next;
if (fast == slow){
break;
}
}
if (!fast || !fast->next){
return NULL;
}
slow = head;
while(fast != slow){
fast = fast->next;
slow = slow->next;
}
return fast;
}
};
总结
今日学习3小时,感到至少不再害怕写c++了