注:本文的代码实现使用的是 JS(JavaScript),为前端中想使用JS练习算法和数据结构的小伙伴提供解题思路。
描述
给你一个链表,每 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]
解题思路
这道题虽然是困难题,只是在各种边界条件和示例中需要考虑很多,其核心思想就是:
- 按照 k 拆分链表为若干组
- 将每一组链表进行翻转
- 将翻转后的每一组链表进行重链接
本人使用了一个数组nodes
存放了每一组链表的头节点
var reverseKGroup = function(head, k) {
// k 为 1, 无需任何操作,直接返回 head 即可
if(k === 1) return head
// 用于分组的计数节点
let count = 0
let p = head
let nodes = []
// 保存每组链表头节点在给定链表中的上一个节点
let pre = null
// 每一组链表的头节点
let start = head
// 保存 不足 k 个长度的那组链表
let tail = null
// 开始分组
while(p !== null){
count ++
pre = p
p = p.next
// 当前子链表长度为 k 的时候
if(count === k){
// 把这个子链表的头节点放到 nodes 中
nodes.push(start)
count = 0
// 将这个链表的头节点,与其上一个节点断开链接
pre.next = null
// 更新 start 为当前的 p 所指向的节点
start = p
}
// p === null 时, 使用 tail 记录剩余部分的子链表的头节点
// !!! 末尾不足 k 长度的链表并没有保存到 nodes 中
if(p === null) tail = start
}
// 对每个子链表逆序
nodes = nodes.map(item => reserveList(item))
// 合并子链表
for(let i = 0, len = nodes.length; i < len; i++){
let p = nodes[i]
// 找到当前子链表的最后一个节点,存在 p 中
while(p.next !== null) p = p.next
// i + 1 < len, 说明其后面还有其他子链表,
// 将当前子链表的最后一个节点和下一个子链表的头节点相链接
if(i + 1 < len) p.next = nodes[i + 1]
// i + 1 >= len, 说明其后面没有长度为 k 的子链表了
// 直接将长度不足 k 的部分追加即可
else p.next = tail
}
// 直需要返回第一个子链表的头节点即可
return nodes[0]
};
链表逆序部分的思路可以参考这篇文章 剑指 Offer II 024. 反转链表
// 链表逆序函数
var reserveList = head => {
let p = head
let newHead = null
while(p !== null){
const temp = p.next
p.next = newHead
newHead = p
p = temp
}
return newHead
}