题目描述
给你链表的头节点 head
,每 k
个节点一组进行翻转,请你返回修改后的链表。
k
是一个正整数,它的值小于或等于链表的长度。如果节点总数不是 k
的整数倍,那么请将最后剩余的节点保持原有顺序。
你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。
示例 1:
输入:head = [1,2,3,4,5], k = 2
输出:[2,1,4,3,5]
示例 2:
输入:head = [1,2,3,4,5], k = 3
输出:[3,2,1,4,5]
解题思路
可以根据分治法的解题思路将一个大问题分解成多个小问题(即每个长度为 k
的子链表),递归地解决这些小问题,然后将结果合并,最终解决原始的大问题。
具体步骤如下:
-
确定是否需要反转:
- 首先,检查链表是否为空或者链表中的节点数量是否少于
k
。如果是,那么不需要进行任何反转,直接返回原始的头节点head
。
- 首先,检查链表是否为空或者链表中的节点数量是否少于
-
确定反转区间:
- 使用两个指针
a
和b
遍历链表,确定需要反转的节点区间。a
指向区间的开始,b
指向区间的结束(不包括b
本身)。通过遍历链表,找到第k
个节点,如果找不到足够的节点,则不进行反转。
- 使用两个指针
-
反转节点:
- 确定好需要反转的区间后,调用
reverse
函数来反转这个区间内的节点。reverse
函数通过改变节点的next
指针来实现节点的反转,而不改变节点的值。
- 确定好需要反转的区间后,调用
-
递归处理剩余链表:
- 反转完当前区间的节点后,将反转后的区间的下一个节点(即
a.next
)连接到递归调用reverseKGroup(b, k)
的结果上。这样,递归地处理剩余的链表,直到整个链表都被处理完毕。
- 反转完当前区间的节点后,将反转后的区间的下一个节点(即
-
连接反转后的链表:
- 递归结束后,每个长度为
k
的子链表都被反转,并且它们通过递归调用的结果连接起来,形成最终的反转链表。
- 递归结束后,每个长度为
-
返回结果:
- 最终,
reverseKGroup
函数返回新的头节点,这个节点指向整个链表的第一个反转后的子链表的头节点。
- 最终,
代码
/**
* Definition for singly-linked list.
* function ListNode(val, next) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
*/
/**
* @param {ListNode} head
* @param {number} k
* @return {ListNode}
*/
var reverseKGroup = function(head, k) {
if(head == null) return null;
// 区间 [a, b) 包含 k 个待反转元素
let a, b;
a = b = head;
for(let i = 0; i < k; i++) {
// 不足 k 个,不需要反转
if(b == null) return head;
b = b.next;
}
// 反转前 k 个元素
let newHead = reverse(a, b);
// 递归反转后续链表并连接起来
a.next = reverseKGroup(b, k);
return newHead;
};
// 反转区间 [a, b) 的元素,左闭右开
var reverse = function(a, b) {
let pre = null, cur = a;
while(cur != b) {
let next = cur.next;
cur.next = pre;
pre = cur;
cur = next;
}
return pre;
}