第四天
以下题目皆在Carl老师的 代码随想录 中(页面左边有对应题目的目录)有详细讲解,这是博主学习的心得和日志,感兴趣的建议去阅读一下原文。
注意因为是两两交互,两两交换需要他们两前面一个节点来定位,考虑好步骤顺序,比喻成人的话,先保存好头再挪屁股,不然挪完屁股找不到头。
养成先保存链表的初始(下一个节点),再去改变(下一个节点)指向的习惯,避免节点信息丢失
/**
* 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* tmp1 = cur->next;
ListNode* tmp2 = cur->next->next->next;
cur->next =cur->next->next;
//恢复tmp1里的节点
cur->next->next = tmp1;
//恢复tmp2里的节点
cur->next->next->next = tmp2;
//向前移两位,准备下一轮交互,因为是两两交互,两两交换需要他们两前面一个节点来定位他们的位置
cur = cur->next->next;
}
// 不能直接return head,因为旧的head已经交换了,所以只能用dummyhead->next来找新的head
ListNode* result = dummyhead->next;
delete dummyhead;
return result;
}
};
让快指针先走n+1步,和还在原地的慢指针构建一个长为n+1的“窗口”,随后快慢指针一起移动,直到停止,慢指针所在位置距离快指针n+1,即慢指针停在了倒数第n+1的节点上,即倒数第n节点的前一位节点,这也是为什么是n+1,这样才能做删除下一个节点(倒数第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* dummyhead = new ListNode(0);
dummyhead->next = head;
ListNode* fast = dummyhead;
ListNode* slow = dummyhead;
//先让快指针走n步,创建一个和还在起始点的慢指针形成长为n的窗口:符合倒数n个节点的目标
while(n!=0 && fast!= nullptr)
{
fast = fast->next;
n--;
}
//快指针多走一步,形成n+1的位差窗口,这样让下面一起移动时,slow停在n节点的前一个节点才能做删除操作
fast = fast->next;
//快慢指针一起移动,移动这个n+1的窗口,直到fast到空
while(fast!=nullptr)
{
fast = fast->next;
slow = slow->next;
}
ListNode* tmp = slow->next;
slow->next = slow->next->next;
delete tmp;
return dummyhead->next;
}
};
先分别算长度,然后挪动长的链表指向头节点的指针到与段节点指针对齐,然后遍历判断两个链表的是否有相等的节点(内存地址),因为都是单链表,所以一旦相交,两个链表后面节点就是共用一个地址,因为链表是用头节点确认地址的。
时间复杂度 O(n+m)
/**
* 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* curA = headA;
ListNode* curB = headB;
int lenA = 0, lenB = 0;
while(curA != nullptr){
lenA++;
curA = curA -> next;
}
while(curB != nullptr){
lenB++;
curB = curB ->next;
}
curA = headA;
curB = headB;
int diff = 0;
//对比两个链表的长度,将长的链表的指向头节点的指针往后移diff位,这样两个指针相当于在同一起跑线上
if (lenA > lenB){
diff = lenA - lenB;
while(diff--){
curA = curA->next;
}
} else{
diff = lenB - lenA;
while(diff--){
curB = curB->next;
}
}
//既然两个指针在同一起跑线,任意判断其中一个链表都可以
while (curA != nullptr)
{
//这里判断是否都指向同一个节点,如果同一个节点(内存地址),那就是相交
if (curA == curB){
return curA;
} else {
//继续向后遍历
curA = curA->next;
curB = curB->next;
}
}
//上面无返回交点,就是不相交,返回NULL
return NULL;
}
};
递归思路可以参考
可以这样理解,两个指针A, 和B,分别从两个链表头出发开始遍历,遍历完自己所在线路这里假设A就去另一条路B去遍历,因为我们本质要找的是两个是否有共用的一个节点内存地址,所以在这个同时遍历的过程中,如果两个链表有相交,两个指针总会遍历到同一个节点,即A, B 同时分别遍历(线路A+B) (线路B+A)的路线,遇到相同地址,返回节点。
时间复杂度 O(n+m) 相当于遍历了(A+B)路线上所有的节点
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode *A = headA, *B = headB;
while (A != B) {
A = A != nullptr ? A->next : headB;
B = B != nullptr ? B->next : headA;
}
return A;
}
};
作者:Krahets
链接:https://leetcode.cn/problems/intersection-of-two-linked-lists-lcci/solutions/1190240/mian-shi-ti-0207-lian-biao-xiang-jiao-sh-b8hn/
/**
* 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* fast = head;
ListNode* slow = head;
//判断->next因为快指针一步走两个节点
while(fast != nullptr && fast->next != nullptr){
fast=fast->next->next;
slow=slow->next;
//快慢指针相遇之后,由推算得知,头节点到环入口的距离=相遇点往前距环入口剩余的距离
if(fast == slow)
{
//构建一个从头节点出发的指针
ListNode* cur=head;
while(cur != slow)
{
//因为slow是正常的遍历速度,所以直接用slow遍历,一起先前走,就一定会在环入口相遇
slow=slow->next;
cur=cur->next;
}
//返回环的入口
return cur;
}
}
return NULL;
}
};