K 个一组翻转链表
1、题目
-
题目说明
- 给你一个链表,每 k 个节点一组进行翻转,请你返回翻转后的链表。
- k 是一个正整数,它的值小于或等于链表的长度。
- 如果节点总数不是 k 的整数倍,那么请将最后剩余的节点保持原有顺序。
-
示例:
- 给你这个链表:1->2->3->4->5
- 当 k = 2 时,应当返回: 2->1->4->3->5
- 当 k = 3 时,应当返回: 3->2->1->4->5
-
说明:
- 你的算法只能使用常数的额外空间。看这图应该就清楚了,In-place 就是常数内存,Out-place 就是额外内存,说白了让你用 In-place 方式的算法来解决问题。
- 你不能只是单纯的改变节点内部的值,而是需要实际进行节点交换。
2、代码
2.1、代码思路
-
以两个为一组讲解思路,其他情况类似
- curNode 指向当前的节点
- nextNode 为 curNode 的下一个节点
- reverseHead 指向每一段子链表的首节点
- reverseTail 指向每一段子链表的尾节点
-
下面举例说明一下程序的大致流程,以 K = 2 为例讲解
- 原链表如下
- 初始操作:
- reverseHead 为子链表的链表头,可以看作是子链表的头结点,初始化时是自己 new 出来的
- reverseTail 每次初始化时,指向子链表的首节点,随着翻转的进行,链表首节点自然就变成了尾节点
- 执行第一次翻转后:将 curNode 指向的节点放在 reversehead 之后 ,curNode、nextNode 指针后移
- 执行第二次翻转:
- 将 curNode 指向的节点放在 reversehead 之后 ,curNode、nextNode 指针后移
- 此时赶紧将原链表的头结点 head 指向 reverseHead ,因为这时候 reverseHead 指向的节点是新的首节点,不珍惜这次,之后就没机会啦
- 此时已经执行了两次翻转,需要重新初始化子链表的头结点和尾节点
- reverseHead 指向之前的 reverseTail ,作为子链表新的头结点
- reverseTail 指向 curNode ,随着翻转的进行,reverseTail 会成为子链表的尾节点
- 注意:如果链表长度不是 K 的整数倍,那么退出循环后需要将 reverseHead 指向 curNode ,否则后面的子链表将丢失。。。
2.2、链表节点的定义
// 节点
class Node {
public Integer data;
public Node next;
public Node(Integer data) {
this.data = data;
}
public String toString() {
return data.toString();
}
}
2.3、K 个一组翻转链表
// 链表类
class SingleLinkedList {
private Node head = new Node(0);
public void add(Node node) {
// 首节点指针不能移动哦,需要定义辅助指针
Node preNode = head;
while (preNode.next != null) {
preNode = preNode.next;
}
preNode.next = node;
}
public void show() {
// 首节点指针不能移动哦,需要定义辅助指针
Node curNode = head.next;
while (curNode != null) {
System.out.print(curNode.data + "-->");
curNode = curNode.next;
}
System.out.println();
}
public int length() {
// 首节点指针不能移动哦,需要定义辅助指针
Node curNode = head.next;
// 链表长度
int len = 0;
while (curNode != null) {
len++;
curNode = curNode.next;
}
return len;
}
public void reverse(int gap) {
int len = length();
// gap 过大
if (len < gap) {
return;
}
// 链表中没有元素或者只有一个元素
if (head.next == null || head.next.next == null) {
return;
}
// 新的头结点和尾节点
Node reverseHead = new Node(0);
Node reverseTail = new Node(0);
// 当前正在遍历的节点
Node curNode = head.next;
// 当前节点的下个节点
Node nextNode = curNode.next;
// reverseTail 是每个子链表的第一个元素
reverseTail = curNode;
// 翻转计数器,记录当前一共翻转了多少个节点
int reverseCount = 0;
while (curNode != null) {
// 将当前正在遍历的节点挂在 reverseHead 之后
nextNode = curNode.next;
curNode.next = reverseHead.next;
reverseHead.next = curNode;
// 指针后移,计数器加 1
curNode = nextNode;
reverseCount++;
// 到达指定个数,则重新需要为 reverseHead 和 reverseTail 重新赋值
if (reverseCount % gap == 0) {
// 如果是第一次到达指定个数,则将头指针指向首节点,否则后面就没机会了。。。
if (reverseCount / gap == 1) {
head.next = reverseHead.next;
}
// 头指针指向左边链表尾节点
reverseHead = reverseTail;
// 尾指针指向右边链表的首节点
reverseTail = curNode;
// 如果不足 gap 个,则推出,不再继续翻转
if (reverseCount + gap > len) {
break;
}
}
}
// 如果不足 gap 个,不翻转,直接连接
if (reverseCount != len) {
reverseHead.next = curNode;
}
}
}
2.4、代码测试
- 代码
public static void main(String[] args) {
SingleLinkedList singleLinkedList = new SingleLinkedList();
singleLinkedList.add(new Node(1));
singleLinkedList.add(new Node(2));
singleLinkedList.add(new Node(3));
singleLinkedList.add(new Node(4));
singleLinkedList.add(new Node(5));
System.out.println("翻转前~~~");
singleLinkedList.show();
System.out.println("翻转后~~~");
singleLinkedList.reverse(2);
singleLinkedList.show();
}
- 程序运行结果
翻转前~~~
1-->2-->3-->4-->5-->
翻转后~~~
2-->1-->4-->3-->5-->
2.5、全部代码
public class ReverseListWithK {
public static void main(String[] args) {
SingleLinkedList singleLinkedList = new SingleLinkedList();
singleLinkedList.add(new Node(1));
singleLinkedList.add(new Node(2));
singleLinkedList.add(new Node(3));
singleLinkedList.add(new Node(4));
singleLinkedList.add(new Node(5));
System.out.println("翻转前~~~");
singleLinkedList.show();
System.out.println("翻转后~~~");
singleLinkedList.reverse(2);
singleLinkedList.show();
}
}
// 链表类
class SingleLinkedList {
private Node head = new Node(0);
public void add(Node node) {
// 首节点指针不能移动哦,需要定义辅助指针
Node preNode = head;
while (preNode.next != null) {
preNode = preNode.next;
}
preNode.next = node;
}
public void show() {
// 首节点指针不能移动哦,需要定义辅助指针
Node curNode = head.next;
while (curNode != null) {
System.out.print(curNode.data + "-->");
curNode = curNode.next;
}
System.out.println();
}
public int length() {
// 首节点指针不能移动哦,需要定义辅助指针
Node curNode = head.next;
// 链表长度
int len = 0;
while (curNode != null) {
len++;
curNode = curNode.next;
}
return len;
}
public void reverse(int gap) {
int len = length();
// gap 过大
if (len < gap) {
return;
}
// 链表中没有元素或者只有一个元素
if (head.next == null || head.next.next == null) {
return;
}
// 新的头结点和尾节点
Node reverseHead = new Node(0);
Node reverseTail = new Node(0);
// 当前正在遍历的节点
Node curNode = head.next;
// 当前节点的下个节点
Node nextNode = curNode.next;
// reverseTail 是每个子链表的第一个元素
reverseTail = curNode;
// 翻转计数器,记录当前一共翻转了多少个节点
int reverseCount = 0;
while (curNode != null) {
// 将当前正在遍历的节点挂在 reverseHead 之后
nextNode = curNode.next;
curNode.next = reverseHead.next;
reverseHead.next = curNode;
// 指针后移,计数器加 1
curNode = nextNode;
reverseCount++;
// 到达指定个数,则重新需要为 reverseHead 和 reverseTail 重新赋值
if (reverseCount % gap == 0) {
// 如果是第一次到达指定个数,则将头指针指向首节点,否则后面就没机会了。。。
if (reverseCount / gap == 1) {
head.next = reverseHead.next;
}
if (reverseCount + gap > len) {
break;
}
reverseHead = reverseTail;
reverseTail = curNode;
}
}
if (reverseCount == len) {
reverseTail.next = null;
} else {
reverseTail.next = curNode;
}
}
}
// 节点
class Node {
public Integer data;
public Node next;
public Node(Integer data) {
this.data = data;
}
public String toString() {
return data.toString();
}
}