题目描述
给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。
k 是一个正整数,它的值小于或等于链表的长度。
如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。
分析
输入:链表头节点 head, 分组长度 k
输出:翻转之后的链表头节点
解题思路
这题实际上考察的是链表操作的时候,你能不能考虑到所有的细节,这个题目里我们可以抽出下面几个知识点:链表翻转,保持链表节点的引用,dummyHead 的使用。
我们在解题的过程再去看具体的知识点。
那我们先说下思路,既然是 k 个为一组,那我们肯定可以把每 k 个 node 作为一组翻转的节点放在一起,然后以每组的 head 来作为翻转开始的节点。
那么在开始翻转之前,我们要看接下来待翻转的这组节点的数量到底还够不够 k 个,如果不够的话,就不翻转了,这块在代码里肯定是个 break while 循环♻️的过程。
这就是整体的思路,在分析完整体的思路之后,我们可以看看,到底怎么翻转呢?
那比如我们要像图片里那样翻两个节点,很显然,我们在需要一个翻转链表方法之余,也需要注意⚠️前边一个节点和后边一个节点。然而还需要注意的一个问题呢,是我们需要考虑💭只有一个节点的情况,这也是链表问题都需要关注的。
解决链表长度为 1 的问题,我们可以用一个 dummyHead 解决,这样删除、翻转 head 的时候,我们都不需要判断长度 1 的情况了~
那我们着重看下翻转,翻转的代码可以写成一个模版,每次遇到翻转链表的问题时,就用固定的套路就可以了。
设置一个前驱节点 pre,当前节点 cur,先让 pre = null。
举个例子,我要翻转下面这个链表, k = 2:
那如果我要翻 3,4 两个节点的话,实际上就是要让 4 的
next 指向 3, 3 的 next 再指向 3 的前一个节点,然后把要操作的区间整体向后移动一个,也就是 pre = 4, cur = cur.next
所以很明显 3 的前一个节点是 null,也就是 pre 的初始值。
我们用代码表示下翻转的过程
function reverse(head) {
// 把前驱节点设成 null,当前节点 cur 设为 head
let cur = head,
pre = null;
// 开始翻转的过程
// 可以理解为 while 循环一次,那么 cur 指向的节点的 next,我们会把它给改成指向
// pre
// 所以每个 while 循环结束后,都会让 cur 向前走一步,继续改变后边 node 的 next
// 直到 cur 指向了 null
while (cur) {
const tmp = cur.next
cur.next = pre
pre = cur
cur = tmp
}
return pre
}
那我们考虑下前后两个节点的问题。
在指定的区间翻转完毕之后,我们可以看到 cur 是整个区间后边的第一个节点,我们应该让还未翻转时候的 head.next 指向这个 node。
再翻转结束后,我们还需要返回新的 head,并且让被翻转链表的前驱指针的 next 指向它。
最后,我们考虑下翻转之前去判断剩余节点够不够 k 个。
我们来看完整代码:
代码
/**
* 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) return null;
const dummyHead = new ListNode(null, head);
let pre = dummyHead;
do {
// 不停地翻转链表
pre.next = reverseList(pre.next, k);
// 直到剩余节点数量不够 k 个,这个需要在每次翻转后检查一下,翻转之前检查
for (let i = 0; i < k; i++) {
// 如果有 k 个,就一直向后移动 pre,直到找到下一个翻转的 head
pre = pre && pre.next;
}
if (!pre) break;
} while (true);
// 翻转节点的代码
function reverseList(head, k) {
let pre = head,
cur = head,
count = k;
// Check if remaining nodes serves k, including cur
while (--count) {
pre = pre && pre.next;
}
if (!pre) return head;
pre = null;
while (k--) {
const tmp = cur.next;
cur.next = pre;
pre = cur;
cur = tmp;
}
// 指向后继节点
head.next = cur;
return pre;
}
// 返回 dummyHead 的 next,这是为了处理原始的 head 节点被处理的情况,很方便~
return dummyHead.next;
};