「这是我参与11月更文挑战的第30天,活动详情查看:2021最后一次更文挑战」。
题目
链接:leetcode-cn.com/problems/re…
给你单链表的头指针 head 和两个整数 left 和 right ,其中 left <= right 。请你反转从位置 left 到位置 right 的链表节点,返回 反转后的链表 。
示例 1:
输入: head = [1,2,3,4,5], left = 2, right = 4 输出: [1,4,3,2,5]
示例 2:
输入: head = [5], left = 1, right = 1 输出: [5]
提示:
- 链表中节点数目为
n 1 <= n <= 500-500 <= Node.val <= 5001 <= left <= right <= n
进阶: 你可以使用一趟扫描完成反转吗?
解题思路
思路1
递归的思路就是不断缩小问题的规模。
我们可以把left的位置逐步缩小到1,把此时left节点当作头节点,那么剩下的问题就是以left开头,反转前right个链表。
所以可以先实现一个反转前N个链表的函数,在将left===1时反转后的链表返回给上一层head.next。
代码
/**
* 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 s = null; // 可以理解为n+1之后的链表
function reverseN(node, n){
if(n === 1){
s = node.next;
return node;
}
let last = reverseN(node.next, n-1);
node.next.next = node;
node.next = s;
return last;
}
function reverseBetween(head: ListNode | null, left: number, right: number): ListNode | null {
// 当left等于1,那么就是反转以left开头,前right个链表。
if(left === 1){
return reverseN(head, right);
}
// 逐步缩小头节点left位置,当left缩小至1时,即为起点。
// 在缩小头节点位置时,剩余链表长度也在缩小,所以right的位置也应当相应缩小。
head.next = reverseBetween(head.next, left-1, right-1);
return head;
};
思路2
- 给定一个链表 头结点 head
- 将链表 [left, right] 区间进行反转
本题 与「206. 反转链表」类似。
pre,left的上一个节点 ,[0 ->pre]本题是定点 不会根据结点反转变化对于结点的位置cur指向当前要翻转的链表结点nextcur的下一个结点
本题无需定义后继结点
var reverseBetween = function(head, left, right) {
// 1
const dummy_node = new ListNode(-1);
dummy_node.next = head;
// 2
let pre = dummy_node;
for (let i = 0; i < left - 1; ++i) {
pre = pre.next;
}
// 3
let cur = pre.next;
for (let i = 0; i < right - left; ++i) {
const next = cur.next;
cur.next = next.next;
next.next = pre.next;
pre.next = next;
}
return dummy_node.next;
};
- 定义一个哑点,放在head前 (防止边界换位置问题)
- 寻找pre的位置,初始化位置是哑点位置
- 将cur的下一个结点插入到pre后面(right - left 次) cur位置每次反转以后会变化