今天分享两种方法:
1.头插法
2.穿针引线法
1、头插法
顾名思义,就是遍历区间内的节点,然后依次插在区间左节点的前一个节点后边。
//区间内链表反转
//方法1;头插法
private static ListNode reverseBetween1(ListNode head, int left, int right) {
ListNode ans = new ListNode(-1);
ans.next = head;
ListNode pre = ans; //pre指向虚拟头结点
//pre向前走i步,到达区间左节点的前一个节点停下,方便区间节点的插入
for (int i = 0; i < left - 1; i++) {
pre = pre.next;
}
ListNode cur = pre.next; //用cur结点标记pre的下一个结点,及区间的左节点,替代pre继续向后遍历
for (int i = 0; i < right - left; i++) { //继续向后遍历right-left个节点,
// 目的是将区间内的所有节点都插入到pre的后边
ListNode next = cur.next;
cur.next = next.next;
next.next = pre.next;
pre.next = next;
}
return ans.next;
}
2、穿针引线法
第一步:根据区间结点,将原始链表分为三部分(即头链表,区间链表,尾链表)。
第二步:将区间结点部分进行链表翻转
第三步:按顺序(头链表,反转后的区间链表,为链表)连接,最后返回ans.next。
ans是虚拟头结点。
代码中有详细的解释,方便理解
//方法2:穿针引线法
private static ListNode reverseBetween2(ListNode head, int left, int right) {
ListNode ans = new ListNode(-1);
ans.next = head;
ListNode pre = ans;
//pre走left-1步,走到left节点的前一个结点
for (int i = 0; i < left - 1; i++) {
pre = pre.next;
}
//标记 pre是left前一个节点,同时又是第一个链表的尾结点
// 这么写的目的是:防止pre节点的丢失,用firstLast来代替pre继续遍历
ListNode firstLast = pre;
//firstLast走到right节点
for (int i = 0; i < right - left + 1; i++) {
firstLast = firstLast.next;
}
//经过for循环后firstLast指向区间链表的尾结点
//newLinkedList指向区间链表的头节点,加上 firstLast.next=null; 这行代码,使其表示区间链表
ListNode newLinkedList = pre.next;
//lastLinkedList是第三部分代码,及区间链表后边的链表。这么定义方便后边的链表重组
ListNode lastLinkedList = firstLast.next;
firstLast.next=null;
//反转区间链表
reverseList(newLinkedList);
pre.next=firstLast;
newLinkedList.next=lastLinkedList;
return ans.next;
}