目录
嵌入式学习分享个人主页:Orion嵌入式随想录 - 小红书 (xiaohongshu.com)
24.两两交换链表中的节点
-
文章讲解:代码随想录
指向改变
-
解题思路
-
初始时,cur指向虚拟头结点,然后进行如下三步:
-
-
解题步骤
-
设置虚拟头节点
-
设置
cur
操作指针开启循环,每次操作指针指向待交换两节点的前一个节点。故偶数cur->next != nullptr
,奇数cur->next->next != nullptr
。 -
需要断开并重新连接1节点和3节点。故需要临时保存1节点位置
cur->next
,3节点位置cur->next->next->next;
-
执行三个步骤,0连2,2连1,1连3
-
操作指针移动两位,指向下一组待交换两节点的前一个节点
-
返回头节点,释放虚拟节点
-
-
代码注意
-
在执行转换步骤时,前一步已经连上,下一个节点直接用next找位置,找的为之前保存的位置
-
-
代码一:虚拟头节点
// 时间复杂度:O(n)
// 空间复杂度:O(1)
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
ListNode* dummyHead = new ListNode(0); // 设置一个虚拟头结点
dummyHead->next = head; // 将虚拟头结点指向head,这样方便后面做删除操作
ListNode* cur = dummyHead;
while(cur->next != nullptr && cur->next->next != nullptr) {
ListNode* tmp = cur->next; // 记录临时节点
ListNode* tmp1 = cur->next->next->next; // 记录临时节点
cur->next = cur->next->next; // 步骤一
cur->next->next = tmp; // 步骤二
cur->next->next->next = tmp1; // 步骤三
cur = cur->next->next; // cur移动两位,准备下一轮交换
}
ListNode* result = dummyHead->next;
delete dummyHead;
return result;
}
};
19.删除链表的倒数第N个节点
-
文章讲解:代码随想录
快慢指针法
-
解题思路
-
双指针的经典应用,如果要删除倒数第n个节点,让fast移动n步,然后让fast和slow同时移动,直到fast指向链表末尾。删掉slow所指向的节点就可以了。
-
首先定义fast指针和slow指针,初始值为虚拟头结点
-
fast首先走n + 1步 ,同时移动时slow才能指向删除节点的上一个节点(方便做删除操作,相当于多倒数一个,n+1)
-
fast和slow同时移动,直到fast指向末尾
-
删除slow指向的下一个节点
-
-
解题步骤
-
创建虚拟头节点
-
定义快慢指针
-
设定快指针先走的长度n+1,循环快指针移动
-
快慢指针同时移动,直到fast为空,slow到目标节点的前一个节点
-
删除目标节点
-
返回头节点
-
-
代码注意
-
while(n--
&& fast != NULL
)
要对fast的next处理 避免操作空指针,循环内都应该判断
-
-
代码一:快慢指针
// 时间复杂度: O(n)
// 空间复杂度: O(1)
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* dummyHead = new ListNode(0);
dummyHead->next = head;
ListNode* slow = dummyHead;
ListNode* fast = dummyHead;
while(n-- && fast != NULL) {
fast = fast->next;
}
fast = fast->next; // fast再提前走一步,因为需要让slow指向删除节点的上一个节点
while (fast != NULL) {
fast = fast->next;
slow = slow->next;
}
slow->next = slow->next->next;
// ListNode *tmp = slow->next; C++释放内存的逻辑
// slow->next = tmp->next;
// delete tmp;
return dummyHead->next;
}
};
106. 链表相交
-
文章讲解:代码随想录
指针对齐法
-
解题思路
-
目前curA指向链表A的头结点,curB指向链表B的头结点
- 求出两个链表的长度,并求出两个链表长度的差值,然后让curA移动到,和curB 末尾对齐的位置
-
此时比较curA和curB是否相同,如果不相同,同时向后移动curA和curB,如果遇到curA == curB,则找到交点。否则循环退出返回空指针。
-
-
解题步骤
-
初始化操作指针为两个头节点
-
求两个链表长度,并重新初始化操作指针
-
计算长度差,移动长链表的操作指针,末尾对其
-
循环内更新,找到则返回结束
-
-
代码注意
-
涉及指针操作注意要判断是否为空
-
进入循环先判断是否相等再更新,否则会丢失刚对其就是相交的情况
-
-
代码一:指针对齐
// 时间复杂度:O(n + m)
// 空间复杂度:O(1)
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;
}
};
142.环形链表II
快慢指针法
-
解题思路
-
可以使用快慢指针法,分别定义 fast 和 slow 指针,从头结点出发,fast指针每次移动两个节点,slow指针每次移动一个节点,如果 fast 和 slow指针在途中相遇 ,说明这个链表有环。
-
fast 走两个节点,slow走一个节点,其实相对于slow来说,fast是一个节点一个节点的靠近slow的,有环的话,一定会在环内相遇,而不是永远的错开。
-
假设从头结点到环形入口节点 的节点数为x。 环形入口节点到 fast指针与slow指针相遇节点 节点数为y。 从相遇节点 再到环形入口节点节点数为 z。
-
相遇时: slow指针走过的节点数为:
x + y
, fast指针走过的节点数:x + y + n (y + z)
,n为fast指针在环内走了n圈才遇到slow指针,(y+z)
为 一圈内节点的个数A。 -
因为fast指针是一步走两个节点,slow指针一步走一个节点, 所以 fast指针走过的节点数 = slow指针走过的节点数 * 2:
(x + y) * 2 = x + y + n (y + z)
两边消掉一个(x+y)
:x + y = n (y + z)
-
因为要找环形的入口,x表示头结点到环形入口节点的的距离。将x单独放在左面:
x = n (y + z) - y
,再从n(y+z)
中提出一个(y+z)
来,整理公式之后为如下公式:x = (n - 1) (y + z) + z
(注意这里n一定是大于等于1的,因为 fast指针至少要多走一圈才能相遇slow指针。当 n为1的时候,公式就化解为x = z
) -
在相遇节点处,定义一个指针index1,在头结点处定一个指针index2。让index1和index2同时移动,每次移动一个节点, 那相遇的地方就是环形入口的节点。(n如果大于1,index1指针在环里多转了(n-1)圈再遇到index2,相遇点依然是环形的入口节点。)
-
-
解题步骤
-
定义快慢指针指向头节点
-
外循环找相遇,判断
fast != NULL && fast->next != NULL
因为fast快且两步走 -
更新快慢指针
-
找到相遇位置放index1,头节点index12
-
内循环找入口,判断
index1 != index2
-
更新索引位置
-
索引相等跳出循环,返回相同索引位置
-
-
代码注意
-
进循环先更新位置才可以产生相遇并进行判断
-
在大循环内快慢节点前进的时候,每一次及时判断相遇位置,同时找入口并返回终止循环。不可以写在大循环外,不然造成资源浪费无止境加。
-
代码一:快慢指针
// 时间复杂度: O(n),快慢指针相遇前,指针走的次数小于链表长度,快慢指针相遇后,两个index指针走的次数也小于链表长度,总体为走的次数小于 2n
// 空间复杂度: O(1)
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
ListNode* fast = head;
ListNode* slow = head;
while(fast != NULL && fast->next != NULL) {
slow = slow->next;
fast = fast->next->next;
// 快慢指针相遇,此时从head 和 相遇点,同时查找直至相遇
if (slow == fast) {
ListNode* index1 = fast;
ListNode* index2 = head;
while (index1 != index2) {
index1 = index1->next;
index2 = index2->next;
}
return index2; // 返回环的入口
}
}
return NULL;
}
};
(说明:基于代码随想录课程学习,部分内容引用自代码随想录文章)