指定区间反转,是面试的高频题,其是基本链表反转的变形题,解决此类问题,主要有2种思路,一是头插法,本质是利用辅助头节点进行反转,二是穿针引线法,本质是不带头节点的链表反转。
头插法
public static ListNode reverseBetween3(ListNode head, int left, int right) {
//思路:和直接插在头节点后面的头插法有点不太一样
//指定区间的cur,一直没有改变
//循环次数,right - left
//每次循环结束,将cur.next插入到prev后面
//同时维护好反转链表片段最后一个节点和剩余节点的关系
ListNode dummy = new ListNode(-1);
dummy.next = head;
ListNode prev = dummy;
for (int i = 0; i < left - 1; i++) {
prev = prev.next;
}
ListNode cur = prev.next;
ListNode next = null;
for (int i = 0; i < right - left; i++) {
next = cur.next;
cur.next = next.next;
next.next = prev.next;
prev.next = next;
}
return dummy.next;
}
穿针引线法
public static ListNode reverseBetween(ListNode head, int left, int right) {
//思路:还是用一个虚拟头节点,避免进行其他条件的讨论,不过在反转链表的时候,不借用虚拟头节点
//首先找到prev,然后找到succ(后继节点)
//然后切出一个子链表,进行反转
//最后进行拼接
ListNode dummy = new ListNode(-1);
dummy.next = head;
//找prev节点
ListNode prev = dummy;
for (int i = 0; i < left - 1; i++) {
prev = prev.next;
}
//找succ
ListNode rightNode = prev;
for (int i = 0; i < right - left + 1; i++) {
rightNode = rightNode.next;
}
ListNode succ = rightNode.next;
rightNode.next = null;//重要,不然会把后面的节点一起反转
//切出一个子链表
ListNode leftNode = prev.next;
//无头节点反转链表
ListNode newNode = null;
ListNode cur = leftNode;
ListNode next = null;
while (cur != null) {
next = cur.next;
cur.next = newNode;
newNode = cur;
cur = next;
}
//拼接链表
prev.next = rightNode;
leftNode.next = succ;
return dummy.next;
}