25. K 个一组翻转链表
给你链表的头节点 head ,每 k 个节点一组进行翻转,请你返回修改后的链表。
k 是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。
你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。
思路
reverseKGroup函数:
- 该函数接收两个参数:一个指向链表头节点的指针head和一个整数k,表示每次需要反转的节点数。
- 首先定义一个指针p指向头节点head,用于遍历链表。
- 然后使用循环从头节点开始向后遍历k个节点,如果遍历到的节点为空(即链表长度不足k),则直接返回头节点head。
- 否则,调用reverse函数对当前的k个节点进行反转,将得到的反转后的链表头节点指针存储在变量ans中。
- 然后,将原来的头节点head的next指针指向递归调用reverseKGroup函数对剩余节点进行处理的结果,即p指针所指向的位置。这样就将原链表中的每k个节点反转后与剩余节点连接起来。
- 最后,返回反转后的链表头节点指针ans。
reverse函数:
- 该函数用于反转链表中指定区间的节点,接收两个参数:指向待反转链表段左端点的指针left和指向右端点的指针right。
- 在函数内部定义了两个指针p和q,分别指向左右端点。
- 使用双指针法,遍历并反转左右端点之间的节点:
- 定义临时指针t保存p的下一个节点。
- 将p指向q,完成节点的反转。
- 移动q指针到p的位置。
- 移动p指针到下一个节点。
- 最后返回反转后链表段的头节点指针q。
通过递归和反转单个链表的方式实现了每k个节点进行反转的功能。
题解
/**
* Definition for singly-linked list.
* struct ListNode {
* int val;
* ListNode *next;
* ListNode() : val(0), next(nullptr) {} // 默认构造函数,初始化val为0,next为空指针
* ListNode(int x) : val(x), next(nullptr) {} // 接收一个整数参数的构造函数,初始化val为x,next为空指针
* ListNode(int x, ListNode *next) : val(x), next(next) {} // 接收一个整数参数和指向下一个节点的指针参数的构造函数,初始化val为x,next为next
* };
*/
class Solution {
public:
// 反转链表中每k个节点的函数
ListNode* reverseKGroup(ListNode* head, int k) {
ListNode* p = head; // 定义指针p指向头节点
// 遍历链表中的每k个节点
for (int i = 1; i <= k; i++) {
if (p == nullptr) // 如果p为空,说明剩余节点数量不足k个,直接返回头节点
return head;
p = p->next; // 否则指针p移动到下一个节点
}
// 反转当前k个节点,并返回反转后的链表头节点指针
ListNode* ans = reverse(head, p);
// 将原头节点指向反转后的剩余节点的结果
head->next = reverseKGroup(p, k);
return ans; // 返回反转后的链表头节点指针
}
private:
// 反转链表中指定区间的节点
ListNode* reverse(ListNode* left, ListNode* right) {
ListNode *p = left, *q = right; // 定义双指针p和q,分别指向左端点和右端点
// 遍历并反转左右端点之间的节点
while (p != right) {
ListNode* t = p->next; // 临时指针t保存p的下一个节点
p->next = q; // 将p指向q,完成反转
q = p; // 移动q指针到p的位置
p = t; // 移动p指针到下一个节点
}
return q; // 返回反转后链表段的头节点指针
}
};
92. 反转链表 II
给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 。
思路
首先创建一个虚拟头节点
dummy
,并初始化指针p
指向虚拟头节点。虚拟头节点的作用是处理反转区间的起始位置可能为头节点的情况。将虚拟头节点的下一个节点指向原链表的头节点
head
,以方便统一处理链表操作。使用循环将指针
p
移动到指定位置,即要反转区间的前一个节点。定义指针
q
指向要开始反转的节点,即p
的下一个节点。进行循环反转指定区间内的节点:
- 在每次循环中,将
q
的下一个节点保存在临时变量t
中。 - 将
q
的下一个节点指向t
的下一个节点,即删除q->next
。 - 将
t
的下一个节点指向p
的下一个节点,即将t
插入到p
之后。 - 将
p
的下一个节点指向t
,完成节点的插入。
- 在每次循环中,将
循环结束后,返回虚拟头节点
dummy
的下一个节点,即反转后的链表头节点。
总体思路是通过创建一个虚拟头节点来处理反转区间的起始位置可能为头节点的情况,并使用指针操作来反转链表中的指定区间。这段代码在时间复杂度上是 O(n),其中 n 为要反转的节点个数。
题解
class Solution {
public:
ListNode* reverseBetween(ListNode* head, int left, int right) {
// 创建虚拟头节点,并初始化指针 p 指向虚拟头节点
ListNode *dummy = new ListNode(0), *p = dummy;
// 将虚拟头节点的下一个节点指向原链表的头节点
dummy->next = head;
// 移动指针 p 到指定位置,使得 p 指向要反转区间的前一个节点
for (int i = 1; i < left; i++)
p = p->next;
// 记录要反转区间的起始节点
ListNode *q = p->next;
// 循环反转指定区间内的节点
for (int i = left; i < right; i++) {
// 将 q 的下一个节点保存在临时变量 t 中
ListNode *t = q->next;
// 将 q 的下一个节点指向 t 的下一个节点,即删除 q->next
q->next = t->next;
// 将 t 的下一个节点指向 p 的下一个节点,即将 t 插入到 p 之后
t->next = p->next;
// 将 p 的下一个节点指向 t,完成节点的插入
p->next = t;
}
// 返回虚拟头节点的下一个节点,即反转后的链表头节点
return dummy->next;
}
};