链表
链表
链表翻转
给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。
输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]
关键思路:
反转一个链表,只需要改变指向就行了
1.首先定义一个当前节点cur,指向头节点head,再定义一个pre指针,指向nullptr,这个初始化很重要
2.之后就可开始反转了,在翻转的时候,我们会将当前cur节点指向pre,但是一旦完成后就与之后的节点断开了,所以我们需要一个临时节点temp预先来保存cur->next
3.之后就是循环遍历了,我们先不确定条件,循环的时候做什么?应该是先进行保存之后的节点,然后将当前节点cur的指向指为pre,自此完成了一个节点翻转,需要进行下一个节点,这时应该先将pre节点变成cur节点,而cur节点应该是预先保留的临时节点
4.哪结束条件呢?应该cur不为空,如果cur为空,说明临时节点也就是某个节点的next下一个节点为空,这时候只要返回pre就行了
class Solution {
public:
ListNode* reverseList(ListNode* head) {
ListNode* pre = nullptr;
ListNode* cur = head;
while(cur != nullptr){
ListNode* temp = cur->next;
cur->next = pre;
pre = cur;
cur = temp;
}
return pre;
}
};
采用递归的方式实现
依旧沿用之前双指针的思路实现。
class Solution {
public:
ListNode* reverse(ListNode* cur, ListNode* pre){
if(cur == nullptr) return pre;
ListNode* temp = cur->next; // 记录后一个节点
cur->next = pre; //将当前的指向指为前一个节点
return reverse(temp,cur); // 开始处理后一个节点后当前节点的指向
}
ListNode* reverseList(ListNode* head) {
return reverse(head, nullptr); // 和之前的初始化逻辑一样
}
};
两两交换链表中的节点
给定一个链表,两两交换其中相邻的节点,并返回交换后的链表。
这道题,引入虚拟头节点会容易很多。

通过引入虚拟头节点来保证元素的交换,首先需要明确,如果我们想交换两个相邻节点,那么需要在这两个节点之前,比如上述例子,cur是在1、2两个节点之前的,我要把cur->next指向2,2中的next指向1(原来的cur中的指向cur->next),1在指向3;然后更新cur,下一次cur应该需要向后移两个节点,cur=cur->next->next。
首先,我们需要解决循环条件的问题,如果链表元素是偶数,那么cur->next,cur->next->next都不为空的情况,那么就要交换,如果是0呢?也是满足条件的,cur->next为空,退出循环;那如果元素是奇数呢?奇数会出现的情况是这样的, cur->next != nullptr,但是cur->next->next = nullptr,这样不交换就行了,没问题。
接下来就开始交换流程了,我们需要一些临时的变量来保存节点,因为在进行交换的时候链接会丢失;那需要保存哪些节点呢?上面的例子告诉我们,我们需要保存1(temp1)、3(temp2)这两个节点,因为我们的链会从cur处断开,cur指向next的next也就是2cur = temp1->next,现在节点情况是 cur --- 2,之后是cur --- 2 --- 1,也就是cur->next->next = temp1,最后是1指3,原先保存的节点temp1->next = temp2,这个temp2就是开始保存下来的2的指向。
class Solution {
public:
ListNode* swapPairs(ListNode* head) {
ListNode* dummyhead = new ListNode(0,head);
ListNode* cur = dummyhead;
while(cur->next != nullptr && cur->next->next != nullptr){
ListNode* temp1 = cur->next;
ListNode* temp2 = cur->next->next->next;
cur->next = temp1->next;
temp1->next->next = temp1;
// cur->next->next = temp1;
temp1->next = temp2;
cur = cur->next->next;
}
return dummyhead->next;
}
};
删除链表的倒数第N个节点
给你一个链表,删除链表的倒数第 n 个结点,并且返回链表的头结点。
输入:head = [1,2,3,4,5], n = 2
输出:[1,2,3,5]
蛮有意思的一道题,首先要明确,删除一个节点,我们需要跑到这个节点的前一个节点。
推荐使用虚拟头节点,这样方便处理删除实际头节点的逻辑。
我们定义一个快慢指针,fast、slow,让快指针先跑n次,然后两个指针一起跑到结尾,这时候,slow所处的位置就是需要删除节点的前面了。
class Solution {
public:
ListNode* removeNthFromEnd(ListNode* head, int n) {
ListNode* dummyhead = new ListNode(0,head);
ListNode* start = dummyhead, *end = dummyhead;
// 两个指针都处在虚拟头节点的位置
for(int i = 0; i < n; ++i){ // 快指针开跑
end = end->next;
}
while(end->next){
// 之后一起跑,直到快指针指向nullptr,说明快指针已经跑到最后一个节点了
end = end->next;
start = start->next;
}
ListNode* temp = start->next; // 删除的节点为慢指针后面的一个节点
start->next = start->next->next;
delete temp;
return dummyhead->next;
}
};
环形链表II
给定一个链表,返回链表开始入环的第一个节点。 如果链表无环,则返回 null
为了表示给定链表中的环,使用整数 pos 来表示链表尾连接到链表中的位置(索引从 0 开始)。 如果 pos 是 -1,则在该链表中没有环。

这道题需要一点数学计算,我们通过定义快慢指针,快指针每次移动2个节点,慢指针每次移动1个节点,如果两个指针相遇,说明有环。我们将从头节点到环入口的距离定义为x,环中相遇定义为y,相遇点到入口为z

可得出公式:(n表示转的圈数)
$$
(x+y) ×2 = x+y+(y+z)×n\
x=n(y+z)-y
$$
慢指针一定是滞后于快指针的,那么如果在环内相遇,那么相遇点这个位置很关键,我们根据公式得到了x=n(y+z)-y这样的式子,也就是说从头节点到入口节点的距离 等于 圈的节点数的公倍数(也许走了1圈或n圈) - 相遇位置到节点位置 y 的距离;也就是如果在这个相遇点和头节点分别开始以一个步长开走,一定会在入口处相遇,只是在环中可能走过多个圈,没问题,公式就是这个意思。
ListNode *detectCycle(ListNode *head) {
ListNode* fast = head;
ListNode* slow = head;
while(fast != nullptr && fast->next != nullptr){
fast = fast->next->next;
slow = slow->next;
while(slow == fast){
ListNode* index1 = slow;
ListNode* index2 = head;
while(index1 != index2){
index1 = index1->next;
index2 = index2->next;
}
return index1;
}
}
return nullptr;
}

浙公网安备 33010602011771号