关卡名称 | 手写链表反转 |
内容 | 1.穿针引线法实现分组反转 |
2.头插法实现分组反转 |
目录
可以说是链表中最难的一个问题了,LeetCode25给你一个链表,每k个节点一组进行反转,请你返回反转后的链表。k是一个正整数,他的值小于或等于链表的长度。如果结点总数不是k个整数倍,那么请将最后剩余的结点保持原有顺序。
进阶:你可以设计一个只使用常数额外空间的算法来解决此问题吗?
你不能只是单纯的改变结点内部的值,而是需要实际进行结点交换。
1.头插法
如果虚拟结点理解了,会发现头插法比上面的穿针引线法更好理解,主要思路还是将其分成已经反转、正在反转、和未反转的三个部分。为了方便循环,我们可以先遍历一遍数组,统计一下元素数量len,确定要分几组n=len/k,然后进行反转,下图表示的是反转结点5时候的过程。
public static ListNode reverseKGroup2(ListNode head, int k) {
// 创建一个虚拟节点作为新链表的头结点
ListNode dummyNode = new ListNode(0);
dummyNode.next = head;
ListNode cur = head;
int len = 0; // 用于记录链表的长度
// 计算链表的长度
while (cur != null) {
len++;
cur = cur.next;
}
int n = len / k; // 计算有几组需要反转
ListNode pre = dummyNode; // pre指向当前处理的组的前一个节点
cur = head; // cur指向当前处理的组的第一个节点
for (int i = 0; i < n; i++) {
for (int j = 0; j < k - 1; j++) { // 对当前组进行反转
ListNode next = cur.next;
cur.next = cur.next.next;
next.next = pre.next;
pre.next = next;
}
pre = cur; // 更新pre为当前组反转后的最后一个节点
cur = cur.next; // 更新cur为下一组的第一个节点
}
return dummyNode.next; // 返回虚拟节点的下一个节点,即为反转后的链表头结点
}
2.穿针引线法
首先,因为要分组反转,我们就一组一组的处理,将其分成已经反转、正在反转和未反转三个部分,同时为了好处理头结点,我们新建一个虚拟头结点,之后我们直接遍历,根据是否为k个找到四个关键位置,并用变量pre、start、end和next标记,如下:
接着我们就可以对上图中颜色部分进行反转了,我们如果将end.next=null,那颜色部分可以直接复用前面的3的实现来完成反转。注意上图中指针的指向和几个变量的位置,head表示传给反转方法的参数,结构如下图所示:
完成之后,我们要将采下来的部分再缝合到原始链表上,这就需要调整指针指向了,同样注意指针的指向:
最后调整一下几个变量的位置,为下一次做准备:
代码如下:
public static ListNode reverseKGroup(ListNode head, int k) {
// 创建一个虚拟节点作为新链表的头结点
ListNode dummy = new ListNode(0);
dummy.next = head;
ListNode pre = dummy; // pre指向当前处理的组的前一个节点
ListNode end = dummy; // end指向当前处理的组的最后一个节点
// 循环直到end的下一个节点为空
while (end.next != null) {
// 移动end指针到当前组的最后一个节点
for (int i = 0; i < k && end != null; i++) {
end = end.next;
}
// 如果end为空,表示剩余节点不足一组,跳出循环
if (end == null)
break;
ListNode start = pre.next; // 记录下当前组的第一个节点
ListNode next = end.next; // 记录下一组的第一个节点
end.next = null; // 切断当前组与下一组的连接
pre.next = reverse(start); // 反转当前组,并将pre的next指向反转后的头结点
start.next = next; // 将当前组反转后的尾节点与下一组的头节点连接
pre = start; // 更新pre为下一组的前一个节点
end = pre; // 更新end为pre
}
return dummy.next; // 返回虚拟节点的下一个节点,即为反转后的链表头结点
}
// 反转链表的函数
private static ListNode reverse(ListNode head) {
ListNode pre = null;
ListNode curr = head;
while (curr != null) {
ListNode next = curr.next;
curr.next = pre;
pre = curr;
curr = next;
}
return pre;
}