题解 | #链表内指定区间反转#
链表内指定区间反转
https://www.nowcoder.com/practice/b58434e200a648c589ca2063f1faf58c
import java.util.*;
/**
* 本题的输入是一个listnode,和拆分的两个数字,通过两个数字把listnode进行切割成三段。
* 解题过程将listnode看成一个数组,并进行遍历,
* 通过两个数字对数据切割成三段,不管其中是否有数据,都切割成三段。
* 将三段数据在拼接成一个listnode进行返回。
* 注意:遍历原始listnode时,需要使用的是遍历后剩余节点的数据。
*/
/*
* public class ListNode {
* int val;
* ListNode next = null;
* public ListNode(int val) {
* this.val = val;
* }
* }
*/
public class Solution {
/**
* 反转链表中指定范围的节点。
*
* @param head 链表的头节点。
* @param m 范围开始的节点索引(包含)。
* @param n 范围结束的节点索引(包含)。
* @return 反转指定范围后的链表头节点。
*/
public static ListNode reverseBetween(ListNode head, int m, int n) {
// 如果链表为空或只有一个节点,直接返回原链表
if (head == null || head.next == null) {
return head;
}
// 左侧链表第一个节点
ListNode left = new ListNode(-1);
// 创建虚拟节点用于分割链表-左边部分
ListNode head1 = splitListNode(left, head, m);
// 获取分割后的左侧链表头节点
left = deleteFirstNode(left);
// 中间侧链表第一个节点
ListNode middle = new ListNode(-1);
// 分割并获取要反转的中间部分链表
// 分割并获取要反转的中间部分链表
int num = 0;
if (n - m <= 0) {
num = 0;
} else {
num = n - m + 2;
}
ListNode head2 = splitListNode(middle, head1, num);
// 获取中间部分链表
middle = deleteFirstNode(middle);
// 右侧链表第一个节点
ListNode right = new ListNode(-1);
// 分割并获取右侧链表
ListNode head3 = splitListNode(right, head2, Integer.MAX_VALUE);
// 获取右侧链表头节点,并删除第一个节点
print(left);
print(middle);
print(right);
right = deleteFirstNode(right);
// 反转中间部分链表,并合并到左侧链表
ListNode leftMerge = mergeTwoLists(left, reverseList(middle));
// 将左侧链表、反转后的中间部分链表和右侧链表合并
ListNode rightMerge = mergeTwoLists(leftMerge, right);
// 返回合并后的链表头节点
return rightMerge;
}
/**
* 打印链表中所有节点的值。
* @param p 链表的头节点。类型为ListNode,表示一个链表的节点。
* 该函数没有返回值。
*/
private static void print(ListNode p) {
// 遍历链表,打印每个节点的值
while (p != null) {
System.out.println(p.val);
p = p.next;
}
// 打印分隔线,以区分不同的链表打印输出
System.out.println("------------------------");
}
/**
* 反转链表。
*
* @param head 链表的头节点。
* @return 反转后的链表的头节点。
*/
public static ListNode reverseList(ListNode head) {
// 如果链表为空或只有一个节点,直接返回头节点
if (head == null || head.next == null) {
return head;
}
ListNode temp = head.next; // 要反转的节点,从第二个节点开始
ListNode nextNode = temp.next; // 要反转的节点的下一个节点
head.next =
null; // 断开头节点和要反转节点的连接,防止反转后形成环
// 使用头插法反转链表
while (temp != null) {
temp.next = head;
head = temp;
temp = nextNode;
if (nextNode != null) {
nextNode = nextNode.next;
}
}
return head; // 返回反转后的链表的头节点
}
/**
* 删除链表的第一个节点。
* 该方法接收一个链表的头节点,并删除该链表的第一个节点,然后返回链表的新头节点。
*
* @param listNode 链表的头节点。如果链表为空,则不进行任何操作并返回null。
* @return 返回删除第一个节点后的链表的头节点。如果原链表为空,则返回null。
*/
private static ListNode deleteFirstNode(ListNode listNode) {
// 检查链表是否为空
if (listNode != null) {
// 新的头节点指向原头节点的下一个节点
ListNode newNode = listNode.next;
listNode.next =
null; // 删除原链表的第一个节点,将头节点的next指针置为null
listNode = newNode; // 更新头节点为新的头节点
}
return listNode; // 返回新的头节点
}
public static ListNode mergeTwoLists(ListNode list1, ListNode list2) {
//任意一跳链表为空时,直接返回另外一条链表
if (list1 == null) {
return list2;
} else if (list2 == null) {
return list1;
} else {
//当前层数的返回结果,放在list1后面
list1.next = mergeTwoLists(list1.next, list2);
return list1;
}
}
/**
* 根据 n的值对listnode进行切割,切割时listnode的值是按照当前node的节点进行切割,也就是0到n为一组.
* 比如head原始数据=0-1-2-3-4-5-6,当前指针的节点为2,那就是2之后的n个元素为返回值。
*
* @param head head,遍历的listnode
* @param n n个数
* @return ListNode
*/
private static ListNode splitListNode(ListNode node, ListNode head, int n) {
ListNode currentNode = node;
// 遍历链表,找到拆分点
for (int i = 1; i < n; i++) {
// 如果head为null,说明给定的n超出了链表的长度,直接退出循环
if (head == null) {
break;
}
currentNode.next = head;
currentNode = head;
// 移动到下一个节点
head = head.next;
}
// 断开链表,形成两个独立的部分
currentNode.next = null;
// 返回被拆分出来的后半部分链表的头节点
return head;
}
}
