代码随想录day4|24.两两交换 ;19. 删除链表的倒数第 N 个结点; 02.07链表相交; 142. 环形链表 II
24.两两交换
题目思路
本体的思路与翻转链表的操作是类似的。不同之处在于cur指针每次前进两步。
理解的关键是step1,当我们用指针指向一个节点时,相当于我们(可以)将指向他的箭头删除
实现
点击查看代码
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
ListNode* dummyNode=new ListNode(0,head);
ListNode* cur=dummyNode;
while(cur->next!=nullptr && cur->next->next != nullptr){
ListNode* temp = cur->next->next->next;
ListNode* temp2 = cur->next;
cur->next = cur->next->next;
cur->next->next = temp2;
cur->next->next->next = temp;
cur = cur->next->next;
}
return dummyNode->next;
}
};
19.删除链表的倒数第 N 个结点
题目思路
使用双指针法,快指针比慢指针领先n个位置,当慢指针到达链表底部时,确定倒数第n+1个节点的位置,从而达到目的。
实现
点击查看代码
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* dummyNode = new ListNode(0,head);
ListNode* slow = dummyNode;
ListNode* fast = dummyNode;
while(n--){
fast = fast->next;
}
while(fast->next != nullptr){
fast = fast->next;
slow = slow->next;
}
ListNode* temp = slow->next;
slow->next = slow->next->next;
delete temp;
return dummyNode->next;
}
};
复杂度分析:
- 时间复杂度O(n),n为链表的长度
- 空间复杂度O(1)
其他思路
1.计算链表长度
2.栈
- 思路:遍历链表的同时将所有节点入栈,根据先进后出原则,弹出第n个节点即可达成目的。
- 代码实现:
点击查看代码
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* dummyNode = new ListNode(0,head);
stack<ListNode*> stk;
ListNode* cur = dummyNode;
while(cur){
stk.push(cur);
cur=cur->next;
}
for(int i = 1; i <= n; i++){
stk.pop();
}
cur=stk.top();
ListNode* temp = cur->next;
cur->next = cur->next->next;
delete temp;
return dummyNode->next;
}
};
- 复杂度分析:
时间复杂度:O(n),n为链表的长度
空间复杂度:O(n),n为链表的长度,主要来自于stack的开销
02.07链表相交
1.栈
思路
遍历链表的同时将所有节点入栈,通过对比弹出栈的节点是否相同寻找第一个不相同的节点。
实现
点击查看代码
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
stack<ListNode*> stkA;
stack<ListNode*> stkB;
ListNode* dummyNodeA = new ListNode(0);
dummyNodeA->next = headA;
ListNode* dummyNodeB = new ListNode(0);
dummyNodeB->next = headB;
ListNode* curA = dummyNodeA;
ListNode* curB = dummyNodeB;
while(curA){
stkA.push(curA);
curA = curA->next;
}
while(curB){
stkB.push(curB);
curB = curB->next;
}
while(curA == curB){
curA = stkA.top();
curB = stkB.top();
stkA.pop();
stkB.pop();
}
return curB->next;
}
};
复杂度分析
- 时间复杂度:O(n),n为链表的长度
- 空间复杂度:O(n),n为栈的开销
2.双指针法
142.环形链表II
1.哈希
思路
要查看是否出现过,最直接的方法就是hash。用哈希表记录经过的节点,如果之后遇到此前遍历过的节点,就是环的入口
实现
点击查看代码
class Solution {
public:
ListNode *detectCycle(ListNode *head) {
unordered_set<ListNode *> visited;
while (head != nullptr) {
if (visited.count(head)) {
return head;
}
visited.insert(head);
head = head->next;
}
return nullptr;
}
};
复杂度分析
- 时间复杂度:O(n),n为链表的长度
- 空间复杂度:O(n),n为hash的开销
2.快慢指针
思路
- slow指针每次走一步,fast每次走两步,最终一定会相遇
- 相遇时,fast走的路程f=a+(n+1)b+nc,slow指针走过的路程s=a+(m+1)b+mc,由f=2s可知,a+(n+1)b+nc=2a+2(m+1)b+2mc
a=(n+1-2m-2)b+(n-2m)c
a=(n-2m-1)b+(n-2m)c
a=(n-1)b+nc
a=(n-1)(b+c)+c - 所以创建temp指针从head出发,slow从相遇点出发,最终会在入口处相遇。
实现
点击查看代码
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;
}
};
复杂度分析
- 时间复杂度:O(n),n为链表的长度
- 空间复杂度:O(n),n为栈的开销
链表总结
1.链表常规做法包括栈的使用或者双指针的使用。栈的空间复杂度要高一些。
2.对于插入节点,删除结点,交换节点这些基本操作要做到非常熟练
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?