题目
解题思路
如果需要用递归来做这一道题目,需要理解反转链表和反转链表的前N个元素这两个题目。
- 反转全部链表很容易理解,使用后序遍历,将元素 最后一个节点 返回,并且改变每一个元素的
next
指针。 - 反转前N个元素也并不难,和反转全部类似,但是特别注意的是记住后驱节点,也就是第
N+1
个节点。 - 反转部分元素就是更进一步的综合体,对于反转部分来说就等于是反转全部,但是由于需要记住后驱节点,所以需要使用reverseN,等于是反转前N个节点。那么如何遍历到需要反转的节点呢?可以使用迭代或者递归,当触发条件时调用reverseN即可。
代码
/**
* Definition for singly-linked list.
* class ListNode {
* val: number
* next: ListNode | null
* constructor(val?: number, next?: ListNode | null) {
* this.val = (val===undefined ? 0 : val)
* this.next = (next===undefined ? null : next)
* }
* }
*/
// 先考虑反转前N个节点,需要记住后驱节点
let successor = null;
function reverseN(head: ListNode | null, m: number): ListNode | null {
if (m === 1) {
successor = head.next; // 记录后驱节点
return head;
}
const last = reverseN(head.next, m - 1);
head.next.next = head;
head.next = successor; // head.next = null; 定义的successor刚好满足
return last;
}
// 如果已经实现了反转前N个节点,那么接下来该考虑的是如何使其在第m个节点时擦开始触发。
// 也就是说前M个不会变化,而m-n就会反转,n-len不会变化。
// 其实很好理解,还是可以通过遍历进行触发,使m每次减去1,当m===1时就代表需要触发反转了,此时对于当前的节点就是反转头节点,长度就是n-m
// 需要注意的是,可以使用m-1,n-1,来同步长度。之所以left===1,是因为包含left的节点需要反转。
function reverseBetween(head: ListNode | null, left: number, right: number): ListNode | null {
if (left === 1) {
return reverseN(head, right);
}
head.next = reverseBetween(head.next, left - 1, right - 1)
return head;
};