leetcode 92. 反转链表 II
反转两个数区间的结点
思路:
反转节点最重要的是找到结点的前一个节点,那么这题就需要找到left的前一个节点
然后翻转后要记录到后一个节点的下一个节点,这个记录
翻转后,再拼接
第一步: 找到left位置的节点
通过定义一个虚拟节点,解决边界问题,例如:只有[1,3],那么1的前节点就是空,所以定义一个虚拟节点的值为空即可
class Solution {
public ListNode reverseBetween(ListNode head, int left, int right) {
if (head == null || left == right) {
return head;
}
ListNode dummy = new ListNode(-1);
dummy.next = head;
// 找到left的前一个节点
ListNode prevLeft = dummy;
for (int i = 1; i < left; i++) {
prevLeft = prevLeft.next;
}
ListNode leftNode = prevLeft.next;
}
}
第二步: 找到区间
那么需要反转的区间就是中间这一块,所以定义prev和curr来进行反转,跟反转链表1的题目一样
class Solution {
public ListNode reverseBetween(ListNode head, int left, int right) {
if (head == null || left == right) {
return head;
}
ListNode dummy = new ListNode(-1);
dummy.next = head;
// 找到left的前一个节点
ListNode prevLeft = dummy;
for (int i = 1; i < left; i++) {
prevLeft = prevLeft.next;
}
ListNode leftNode = prevLeft.next;
// 反转[left, right]之间的节点
ListNode curr = leftNode;
ListNode prev = null;
for (int i = left; i <= right; i++) {
ListNode nextTemp = curr.next;
curr.next = prev;
prev = curr;
curr = nextTemp;
}
}
}
第三步: 将这段反转的链表拼接上去
// 将反转部分与原链表连接起来
prevLeft.next = prev;
leftNode.next = curr;
这样就反转完毕啦~
class Solution {
public ListNode reverseBetween(ListNode head, int left, int right) {
if (head == null || left == right) {
return head;
}
ListNode dummy = new ListNode(-1);
dummy.next = head;
// 找到left的前一个节点
ListNode prevLeft = dummy;
for (int i = 1; i < left; i++) {
prevLeft = prevLeft.next;
}
ListNode leftNode = prevLeft.next;
// 反转[left, right]之间的节点
ListNode curr = leftNode;
ListNode prev = null;
for (int i = left; i <= right; i++) {
ListNode nextTemp = curr.next;
curr.next = prev;
prev = curr;
curr = nextTemp;
}
// 将反转部分与原链表连接起来
prevLeft.next = prev;
leftNode.next = curr;
return dummy.next;
}
}
其实我的方法跟这个方法一样,只是获取区间的方法不一样
/**
* 方法1:穿针引线法
*
* @param head
* @param left
* @param right
* @return
*/
public static ListNode reverseBetween(ListNode head, int left, int right) {
// 因为头节点有可能发生变化,使用虚拟头节点可以避免复杂的分类讨论
ListNode dummyNode = new ListNode(-1);
dummyNode.next = head;
ListNode pre = dummyNode;
// 第 1 步:从虚拟头节点走 left - 1 步,来到 left 节点的前一个节点
// 建议写在 for 循环里,语义清晰
for (int i = 0; i < left - 1; i++) {
pre = pre.next;
}
// 第 2 步:从 pre 再走 right - left + 1 步,来到 right 节点
ListNode rightNode = pre;
for (int i = 0; i < right - left + 1; i++) {
rightNode = rightNode.next;
}
// 第 3 步:切断出一个子链表(截取链表)
ListNode leftNode = pre.next;
ListNode succ = rightNode.next;
// 注意:切断链接
pre.next = null;
rightNode.next = null;
// 第 4 步:同第 206 题,反转链表的子区间
reverseList(leftNode);
// 第 5 步:接回到原来的链表中
pre.next = rightNode;
leftNode.next = succ;
return dummyNode.next;
}
/**
* 基本的反转方法
*
* @param head
* @return
*/
public static ListNode reverseList(ListNode head) {
ListNode prev = null;
ListNode curr = head;
while (curr != null) {
ListNode next = curr.next;
curr.next = prev;
prev = curr;
curr = next;
}
return prev;
}
这里反转方法还可以使用递归的方式:
private void reverseLinkedList(ListNode head) {
// 也可以使用递归反转一个链表
ListNode pre = null;
ListNode cur = head;
while (cur != null) {
ListNode next = cur.next;
cur.next = pre;
pre = cur;
cur = next;
}
}
这个方式其实就是在前面两种方式的切断那块代码不进行切断,直接转
public static ListNode reverseBetween2(ListNode head, int left, int right) {
// 设置 dummyNode 是这一类问题的一般做法
ListNode dummyNode = new ListNode(-1);
dummyNode.next = head;
// 前一个节点的指针
ListNode pre = dummyNode;
// 找到left节点的前一个节点
for (int i = 0; i < left - 1; i++) {
pre = pre.next;
}
// 当前节点的指针
ListNode cur = pre.next;
ListNode next;
// 反转left到right之间的节点
for (int i = 0; i < right - left; i++) {
// 保存当前节点的下一个节点
next = cur.next;
// 将当前节点的next指针指向next节点的下一个节点,完成节点交换
cur.next = next.next;
// 将next节点插入到pre节点的后面
next.next = pre.next;
// 将pre节点的next指针指向next节点,即完成插入操作
pre.next = next;
}
return dummyNode.next; // 返回反转后的链表头节点
}
主要是把拼接的步骤提前做好就行了
递归也可以使用这种方式,那么判断就尤为重要了
class Solution {
int i = 0; // 计数器i,用于记录当前遍历到的节点位置
public ListNode reverseBetween(ListNode head, int left, int right) {
i++; // 每次递归进入函数时,计数器i自增1
// 如果i等于right,说明已经递归到了right位置,直接返回head
if (i == right) {
return head;
}
// 如果i小于left,说明还没有到达left位置,继续递归,处理下一个节点
if (i < left) {
head.next = reverseBetween(head.next, left, right);
return head;
// 若i大于等于left,说明已经到达left位置,开始反转链表
} else {
ListNode node = reverseBetween(head.next, left, right); // 递归处理下一个节点
ListNode nex = head.next.next; // 保存head的下两个节点的引用
head.next.next = head; // 将head的下一个节点的next指针指向head,完成反转
head.next = nex; // 将head的next指针指向原来的下两个节点,保持链表连接
return node; // 返回递归处理后的链表头节点
}
}
}
递归看看就好,有点离大谱,干不动