「这是我参与11月更文挑战的第7天,活动详情查看:2021最后一次更文挑战」
经过了前两篇文章的反转链表和反转部分链表的练习,已经对反转链表有了一定的理解,这篇文章就是反转链表的最后一篇,也是难度最大的一篇,是我迄今为止写的第一篇难度为困难的题,如果说直接看这道题,可能还会有所困惑,但是经过了前两篇文章之后,我想拿到这个题,应该足以得心应手。
25. K 个一组翻转链表
给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。
k 是一个正整数,它的值小于或等于链表的长度。
如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。
进阶:
-
你可以设计一个只使用常数额外空间的算法来解决此问题吗?
-
你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。
输入:head = [1,2,3,4,5], k = 2 输出:[2,1,4,3,5]
输入:head = [1,2,3,4,5], k = 3 输出:[3,2,1,4,5]
输入:head = [1,2,3,4,5], k = 1 输出:[1,2,3,4,5]
输入:head = [1], k = 1 输出:[1]
看到这道题,似乎和上一道翻转部分链表相似,如果不是每K节点翻转一次,而是从头开始隔K节点翻转一次,只反转一次的话,那就是上一篇文章中,left=1,right=k。所以我们能想到的就是把链表分段,首先遍历链表得到链表的长度,然后确定可以进行几次K个节点的反转,第一次反转1-K个节点,再反转K+1-2K个节点,直至反转完成。
var reverseKGroup = function (head, k) {
// 按节点数为k把链表分段,每段翻转,最后一段判断节点长度
let link = head;
let n = 0;
// 计算链表的长度
while (link) {
link = link.next;
n++
}
// 获取需要反转几次
let m = Math.floor(n / k)
// 进行m次反转
for (let i = 0; i < m; i++) {
head = reverseBetween(head, i * k + 1, k)
}
return head
};
// 这就是上一篇文章的部分链表反转,
// 如有不懂可查阅“[反转部分链表]:(https://juejin.cn/post/7028817441731903502)”
var reverseBetween = function (head, left, k) {
let dummyNode = new ListNode(-1);
dummyNode.next = head;
let prev = dummyNode;
for (let i = 0; i < left - 1; i++) {
prev = prev.next
}
let curr = prev.next;
for (let i = 0; i < k - 1; i++) {
const next = curr.next;
curr.next = next.next;
next.next = prev.next;
prev.next = next
}
return dummyNode.next
}