今天还是做的力扣,虽然感觉到还是挺亲切,但是刚开始还是挺磕磕绊绊的,总是会出现runtime error,我发现这种问题出现,就需要仔细debug。
思维有点顿顿的,感觉自己不大聪明。
一看提交记录竟然在22年就已经做过这道题了,
但是关于这道题有个疑问就是为什么我在循环外new那个cur和next就会有问题,用这份代码会超出时间限制。
/**
* 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(0);
dummy->next = head;
ListNode* pre = dummy;
ListNode* cur = pre->next;
ListNode* next = pre->next->next;
while(pre->next&&pre->next->next) {
pre->next=next;
cur->next=next->next;
next->next=cur;
pre = cur;
}
return dummy->next;
}
};
这份答案没问题。
/**
* 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(0);
dummy->next = head;
ListNode* pre = dummy;
while(pre->next&&pre->next->next) {
ListNode* cur = pre->next;
ListNode* next = pre->next->next;
pre->next=next;
cur->next=next->next;
next->next=cur;
pre = cur;
}
return dummy->next;
}
};
文心一言告诉我:
- 变量初始化的位置:
- 第一份代码中,
cur
、next
和pre
是在循环外部初始化的。这意味着在第一次迭代之后,cur
和next
指向的节点可能不再与当前的pre
相邻。这可能导致在后续的迭代中跳过一些节点或者导致错误的节点链接。- 第二份代码中,
cur
和next
是在每次循环内部初始化的,这确保了cur
和next
始终指向当前pre
指针后的相邻节点。这样每次迭代都基于正确的节点对进行操作。- 循环条件:
- 两份代码中的循环条件都正确地检查了两个相邻的节点是否存在。但由于第一份代码中
cur
和next
的初始化问题,即使循环条件本身没有问题,也可能导致不正确的操作。- 指针更新:
- 在两份代码中,指针的更新逻辑是相似的。但是,由于第一份代码中
cur
和next
的初始化问题,这些更新可能基于错误的节点对进行,从而导致链表结构的错误或不完整的节点交换。- 潜在的内存泄漏:
- 虽然两份代码都没有直接显示内存泄漏(因为
dummy
节点在函数结束时不需要显式删除),但如果第一份代码由于错误操作而丢失了对某些节点的引用,那么这些节点可能会变得不可达,从而导致内存泄漏。然而,这通常不是导致超时的直接原因。- 性能差异:
- 从性能的角度来看,如果第一份代码由于上述原因而未能正确交换所有节点对,那么它可能需要更多的迭代来“修复”链表,从而导致超时。而第二份代码由于每次迭代都基于正确的节点对进行操作,因此通常能够更快地完成任务。
- 可读性和可维护性:
- 第二份代码由于将
cur
和next
的初始化放在循环内部,使得代码更加清晰和易于理解。这也有助于减少错误的可能性,并使代码更易于维护和调试。综上所述,第一份代码可能会因为
cur
和next
变量在循环外部初始化而导致不正确的节点对交换和潜在的链表结构错误,从而可能导致超时。而第二份代码通过将cur
和next
的初始化放在循环内部,确保了每次迭代都基于正确的节点对进行操作,从而更加可靠和高效。
我想大概是由于初始化问题,第一份代码在循环内不知道什么时候就会链接错误了吧。
我自己做出来的,我真棒。
其实就是计算了一下链表的长度,不过debug以后成功运行的感觉特别好。
在刚开始没有看清是倒数第N个节点,误以为是第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(0);
dummy->next = head;
ListNode* slow = dummy;
ListNode* fast = dummy->next;
int size=1,count=0;
while(fast->next) {
fast = fast->next;
size++;
}
count = size-n;
while(count>0) {
count--;
slow=slow->next;
}
fast=slow->next;
slow->next = fast->next;
fast->next = nullptr;
return dummy->next;
}
};
其实这题没怎么看懂。
要注意的一点是返回的是指针≠数值。
看了力扣的双指针解法,天才!化成了追及问题,不过我手动模拟过程的时候其实也没很理解原理。看到了评论里的解释是这样的:
方法二直接简单理解为:
如链表A+链表B=链表C1
链表B+链表A=链表C2
A ->
a1 a2 c1 c2 c3
B ->
b1 b2 b3 c1 c2 c3
C1 ->
a1 a2 c1 c2 c3 b1 b2 b3 c1 c2 c3
C2 ->
b1 b2 b3 c1 c2 c3 a1 a2 c1 c2 c3
此时C1和C2的长度一定相同。 而C1和C2的结尾就一定是相交的链表。
/**
* 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) {
if(!headA||!headB) return nullptr;
ListNode* A=headA;
ListNode* B=headB;
while(A!=B) {
A = A == nullptr ? headB:A->next;
B = B == nullptr ? headA:B->next;
}
return A;
}
};
还有就是计算长度,然后将指针对齐,我没有细做,略看了一下训练营的题解:
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode* curA = headA;
ListNode* curB = headB;
int lenA = 0, lenB = 0;
while (curA != NULL) { // 求链表A的长度
lenA++;
curA = curA->next;
}
while (curB != NULL) { // 求链表B的长度
lenB++;
curB = curB->next;
}
curA = headA;
curB = headB;
// 让curA为最长链表的头,lenA为其长度
if (lenB > lenA) {
swap (lenA, lenB);
swap (curA, curB);
}
// 求长度差
int gap = lenA - lenB;
// 让curA和curB在同一起点上(末尾位置对齐)
while (gap--) {
curA = curA->next;
}
// 遍历curA 和 curB,遇到相同则直接返回
while (curA != NULL) {
if (curA == curB) {
return curA;
}
curA = curA->next;
curB = curB->next;
}
return NULL;
}
};
以前做过,哈希表秒了哈希表秒了哈希表秒了哈希表秒了哈希表秒了哈希表秒了哈希表秒了。
不过就是一开始,以为相交的节点就是值一样,,然后就想了好久,还以为题目的示例有问题。后来发现是指节点,那没事了。
/**
* 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) {
unordered_set<ListNode *> visited;
while(head){
// 需要注意的是返回的是节点而不是值,所以能直接用哈希表
if(visited.count(head)) return head;
visited.insert(head);
head=head->next;
}
return nullptr;
}
};
哦对,哈希表的语法要注意一下(忘了)。
另外还有快慢指针做法,相当于快指针的速度是2,慢指针的速度是1,然后遍历,当快慢指针相遇时的节点的就是那个答案辽!