- 两两交换链表中的节点
https://leetcode.cn/problems/swap-nodes-in-pairs/
首先,当需要对链表进行增删改这样的操作都应该引入虚拟节点,这样可以省去对头节点的特殊处理;其次,cur节点每一次执行交换后都应该直接指向下一个交换后的头节点,因为这样才能够将三个节点都联系起来,用tmp1,tmp2指针进行存储,最终完成交换。
点击查看代码
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 *dummyNode = new ListNode(0, head);
ListNode *cur = dummyNode;
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;
tmp->next = tmp1;
cur = cur->next->next;
}
ListNode *result = dummyNode->next;
delete dummyNode;
return result;
}
};
- 删除链表的倒数第n个节点
https://leetcode.cn/problems/remove-nth-node-from-end-of-list/
首先,拿到这个题目的一般思路就是先遍历一遍知道链表的总长度,在遍历一遍根据总长度去删除第n个节点,时间复杂度也不高,每个元素都至多被遍历两次,是一个好的思路,但是使用快慢指针无疑是对前面学习的学以致用,一般来说这种需要循环两次的题目都值得用双指针的思路去重构;对于双指针的算法来说,可以让fast指针先出发n+1步,因为删除第n个节点必然是要让第n-1个节点去删除,因为这只是一个简单链表。下面的图或许更加清晰。
![截图 2025-07-12 15-38-19]()
点击查看代码
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;
}
};
- 链表相交
https://leetcode.cn/problems/intersection-of-two-linked-lists-lcci/
本题的思路不难,首先是要将两个链表的末端对齐,之后再同时从头节点出发,看看是否有相同的节点,即可判断是否相交。PS:swap函数感觉可以特别记忆一下。
点击查看代码
using namespace std;
struct ListNode {
int val;
ListNode *next;
ListNode(int x) : val(x), next(nullptr) {}
};
class Solution {
public:
ListNode *getIntersectionNode(ListNode *headA, ListNode *headB) {
ListNode *curA = headA;
ListNode *curB = headB;
// 由于是单链表,先统计每个链表的长度
int lenA = 0;
int lenB = 0;
while (curA != nullptr) {
lenA++;
curA = curA->next;
}
while (curB != nullptr) {
lenB++;
curB = curB->next;
}
curA = headA;
curB = headB;
if (lenB > lenA) {
swap(lenA, lenB);
swap(curA, curB);
}
int gap = lenA - lenB;
while (gap--) {
curA = curA->next;
}
while (curA != nullptr && curB != nullptr) {
if (curA == curB) {
return curA;
} else {
curA = curA->next;
curB = curB->next;
}
}
return nullptr;
}
};
- 环形链表
https://leetcode.cn/problems/linked-list-cycle-ii/
首先,我们要判断这个链表是否有环,可以设置一个快指针,一个慢指针,fast每次走2个节点,slow每次走1个,为什么要这样呢,因为这样可以fast会以每次一个节点的速度在环内赶上slow,如果速度是3的话,很有可能过快导致直接超越slow使用更多的时间才能检测出来;其次,就是数学部分的推导,这里就不再赘述,代码随想录网站肯定比我讲的好https://programmercarl.com/0142.%E7%8E%AF%E5%BD%A2%E9%93%BE%E8%A1%A8II.html#%E6%80%9D%E8%B7%AF
点击查看代码
/**
* 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;
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;
}
};