本文已参与「掘力星计划」,赢取创作大礼包,挑战创作激励金。
K 个一组翻转链表
给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。
k 是一个正整数,它的值小于或等于链表的长度。
如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。
进阶:
你可以设计一个只使用常数额外空间的算法来解决此问题吗?
你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。
示例:
输入: head = [1,2,3,4,5], k = 2
输出: [2,1,4,3,5]
这是力扣的一道困难题 . 虽然我第一次做的时候毫不犹豫看了题解 , 但我还是希望诸君可以先进行思考 , 尽力而为 .
首先 , 翻转链表 , 我们得了解且掌握 .
反转链表
给你单链表的头节点 head
,请你反转链表,并返回反转后的链表。
输入: head = [1,2,3,4,5]
输出: [5,4,3,2,1]
链表 , 在js中以对象的形式来实现 .
function ListNode(val, next) {
this.val = (val===undefined ? 0 : val)
this.next = (next===undefined ? null : next)
}
这段创建链表的代码相信各位都能看懂 .
接下来我们开始解决一个普通的翻转链表 .
var reverseList = function(head) {
//先初始化前驱结点
let pre = null;
//初始化目标结点为头结点
let cur = head;
while (cur !== null) {
//用next记录cur的后继结点,在循环中不断可以存着当前结点的后继结点
let next = cur.next;
//翻转指针
cur.next = pre;
//pre往前走一步
pre = cur;
//cur往前走一步
cur = next;
}
//翻转之后 pre就变成了头结点
return pre
};
回到我们本篇的问题 , K个一组翻转链表 , 我们已经完成了翻转链表 , 剩下的就是K个一组了 .
此时真正的难题来临了 :
1.链表进行分组 .
2.对分组的链表进行判断是否满足翻转的数量 .
3.进行局部翻转
4.思考分组链表翻转后的结点的关系 .
1.分组 翻转
因为已经确认了要翻转的链表长度K , 所以我们选择for循环而不是while
for循环内就是基础的翻转链表操作 .
let pre = null,cur = head;
//对该k个链表元素进行反转
for(let i = 0;i<k;i++){
let next = cur.next;
cur.next = pre;
pre = cur;
cur = next;
}
- 判断分组
//K为翻转数
let p = head;
//查找长度是否满足反转的数量
for(let i = 0;i<k;i++){
if(p == null) return head;
p = p.next;
}
-
思考结点之间的关系 . 在每次翻转链表时 , 我们始终let了一个next来存着最后一个翻转元素的下一个 , 而局部的翻转只是切断了这个关系 , 但当局部翻转完成后 , head.next已经到了最末尾 , 它指向下一次翻转的第一个点 . cur即是下一个翻转的第一个元素 , pre则为翻转完成链表中最后一个元素 .
-
递归 .
该有的判断也有了 , 该进行的翻转也有了 , 剩下的只是由小见大 , 把局部转换为整体 . 递归无疑是最容易想到的一个操作 . 就像遍历二叉树一样 , 递归是如此易于理解 .
最后的代码 .
var reverseKGroup = function(head, k) {
let pre = null,cur = head;
let p = head;
for(let i = 0;i<k;i++){
if(p == null) return head;
p = p.next;
}
for(let j = 0;j<k;j++){
let next = cur.next;
cur.next = pre;
pre = cur;
cur = next;
}
head.next = reverseKGroup(cur,k);
return pre;
};
虽然思路上还是容易让人接受 , 但实际在复现思路的过程中也是充满艰辛 . 唯有多次练习 , 从 206. 反转链表 , 92. 反转链表 II , 开始练习 , 地基打好 , 才能将思路转化为代码 , 将思维转化为本能 .
我们一起努力 , 哪有什么天纵奇才 , 只不过是一群秃子在负重前行 .
「欢迎在评论区讨论,掘金官方将在掘力星计划活动结束后,在评论区抽送100份掘金周边,抽奖详情见活动文章」