1. Description
Given the head of a singly linked list and two integers left and right where left <= right, reverse the nodes of the list from position left to position right, and return the reversed list.
给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 。
Follow up: Could you do it in one pass?
2. Analysis
用递归思考:
- 明确递归函数意义:函数接收链表头节点
head
和两个整数left
、right
,完成对left
和right
之间的链表反转后返回新链表头节点 - 问题规模用什么量描述:链表节点个数
- 问题规模如何减小:节点个数减少(递归时传入
head.next
,left-1
,right-1
) - 边界条件:
left == 1
,即头节点为反转开始的节点,此时反转从头结点开始的rigth-left+1
个节点即可——调用reverseN()
由上述分析可知,当触发边界条件调用reverseN()
时,问题发生了转化,下面只需要解决如何反转从头节点开始的n个节点即可。
递归思考:
- 明确递归函数意义:函数接收
head
和n
,完成从head
开始的n
个节点的反转后返回头节点 - 问题规模用什么量描述:链表节点个数
- 问题规模如何减小:节点个数减少(递归时传入
head.next
、n-1
) - 边界条件:
n == 1
,只有一个节点,直接return head
至此,本题从逻辑上已经解决,整体思路为尝试递归解决,发现触发边界条件时转换为一个较为简单的新问题,新问题也可用递归解决。
3. Code
class Solution {
ListNode successor = null;
public ListNode reverseBetween(ListNode head, int left, int right) {
if(left == 1) return reverseN(head, right-left+1);
head.next = reverseBetween(head.next, left-1, right-1);
return head;
}
// 反转从头节点开始的n个节点,返回新链表头节点
private ListNode reverseN(ListNode head, int n){
// 边界条件
if(n == 1) {
successor = head.next;
return head;
}
// 递归调用解决问题规模-1的子问题
ListNode last = reverseN(head.next, n-1);
// 运用子问题解决的结果解决本问题
head.next.next = head;
head.next = successor;
return last;
}
}
4. Complexity
- 时间:O(n);解决每个问题的操作为O(1),问题个数与n相关,故为O(1) * n = O(n)
- 空间:O(n);递归一次堆一次栈,递归层数与问题规模n相关
5. Summary
-
链表是递归式结构,本题尝试递归解决,发现触发边界条件时问题转换为一个反转从头节点开始的n个节点,新问题也可用递归解决。
-
递归原理:递归是一个Top-Down-Top(自顶至下再自下而上)的过程,不停地调用自身使问题规模不断减小至触发边界条件(Top-Down),处理完边界条件后,利用较小规模问题处理结果不断解决规模更大的问题(Down-Top)直到解决本问题。
-
递归的思考路径:
- 递归函数意义(入参、功能、返回值)
- 问题规模用什么量描述(链表问题通常是链表节点个数)
- 问题规模如何减小(链表问题通常传入下一节点
head.next
) - 边界条件(链表问题通常边界条件为只剩一个节点,此时一般问题规模最小)
-
链表题递归代码模板:
函数返回值 递归函数名(参数列表){ 边界条件判断及处理; 调用递归函数解决问题规模-1的子问题并存储结果; 利用子问题解决结果解决本问题; }