LeetCode 92.反转链表||
大家好,我是灵魂画师--茄子。技术水平一般,喜欢画画。
开始今天的正题。
说明:
1 ≤ m ≤ n ≤ 链表长度。
示例:
输入: 1->2->3->4->5->NULL, m = 2, n = 4
输出: 1->4->3->2->5->NULL
1.递归解法
我们先来回顾一下206反转链表的递归解法,由这个解法来延伸出来区间反转链表。
参考下方的拆解结合图解你肯定会理解的
/**
* @param {ListNode} head
* @return {ListNode}
* 拆解
* 1 -> 2 -> 3 -> 4 -> 5 -> NULL
* 1 -> reverseList(2 -> 3 -> 4 -> 5-> NULL)
* 1 -> reverseList(2 -> reverseList(3 -> 4 -> 5 -> NULL))
* ...
* 1 -> reverseList(2 -> reverseList(3 -> reverseList(4 -> reverseList(5 -> NULL))))
* 1 -> reverseList(2 -> reverseList(3 -> reverseList(4 -> 5))) // reverseList(5 -> NULL)由于if(head.next == null) 返回了5
* 1 -> reverseList(2 -> reverseList(3 -> (4 <- 5))) // head.next.next = head // 5 指向4 4指向NULL
|
NULL
1 -> reverseList(2 -> (3 <- 4 <- 5)) // head.next.next = head // 4 指向3 3指向NULL
| X
NULL NULL
1 -> reverseList(2 <- 3 <- 4 <- 5) // head.next.next = head // 3 指向2 2指向NULL
| X X
NULL NULL NULL
1 <- 2 <- 3 <- 4 <- 5 // head.next.next = head // 2 指向1 1指向NULL
| X X X
NULL NULL NULL NULL
NULL <- 1 <- 2 <- 3 <- 4 <- 5
*/
let reverseList = (head) => {
if(head.next == null){
return head
}
let lest = reverseList(head.next);
head.next.next = head
head.next = null
return lest
};
再往下 我们思考反转前N个节点
比如说 我们要反转前3个节点
只要将上面的那种形式变为下面的这种形式即可,根据我们的代码,稍加思考,稍微改动一下即可达成目标。
let reverseListN = (head,n) => {
let next = null;
if(n == 1){ // 当n为1时,返回当前节点,记录n后面的一个节点
next = head.next;
return head;
}
let lest = reverseListN(head.next,n-1);
head.next.next = head; // 设置下一个节点的指向前一个节点
head.next = next; // 记录下的后一个节点
return lest
};
现在让我们回归到我们刚开始想的那个问题 如何反转m-n之间的节点?
假设m为1,那么是不是跟我们反转前n个节点是一模一样的? 我们需要来鉴别的就是m不为1的情况(或者说我们想办法让m为1,就跟截取前n个节点是一样的了)
let next = null;
let reverseListN = (head,n) => {
if(n == 1){ // 当n为1时,返回当前节点,记录n后面的一个节点
next = head.next;
return head;
}
let lest = reverseListN(head.next,n-1);
head.next.next = head; // 设置下一个节点的指向前一个节点
head.next = next; // 记录下的后一个节点
return lest
};
let reverseBetween = (head,m,n) =>{
if(m == 1){ // 如果m为1的话 就跟反转前n个节点是一样的了。
return reverseListN(head,n)
}
// 我们要做的就是前进到m个节点的那个位置 让从m开始反转
head.next = reverseBetween(head.next, m - 1,n - 1);
return head
}
2.循环解法(如果上面的没看明白,可以试试看一下这个方法)
首先,我们还是来回顾一下206反转链表的解法。
/**
* @param {ListNode} head
* @return {ListNode}
*/
let reverseList = (head) => {
let pre = null;
let cur = head;
while(cur != null){
// 先把 next = 2->3->4->5->NULL
let next = cur.next;
// 然后让 cur.next赋值为NULL((cur目前是 1->2->3->4->5->NULL 赋值之后 1->NULL)
cur.next = pre;
// pre 进一位 pre原来是 null 进一位 ({1,next:{null}})
pre = cur;
// cur 原来是 1->2->3->4->5->NULL 进一位 2->3->4->5->NULL
cur = next;
}
return pre;
};
与普通反转链表不同的是,我们需要在m-n的区间内反转。
- 1.找到第m个节点 从第m个节点开始反转。
- 2.反转m-n的长度后 记录n节点的下一个节点。
- 3.将第m个节点指向第n个节点的下一个节点。
// 1 -> 2 -> 3 -> 4 -> 5 -> NULL
// m = 3 n = 5
let reverseBetween = (head,m,n) =>{
let length = n - m; // 记录需要反转的长度
let p = newNode = new ListNode();
p.next = head;
let i = 1;
while( m - i > 0){ // 这段代码循环找到第m个节点的前一个节点,我们需要记录下前一个节点,然后在反转完毕后把他指向回去。这个时候p循环完便是 2 -> 3 -> 4 -> 5 -> NULL
p = p.next;
i++;
}
let pre = p.next; // pre指向m节点
let prior = p; // 保留m的前一个节点。
let letter = p.next; // 这里我们要保存一下m节点,在后面我们可以让m节点指向n节点的下一个节点。
let cur = p.next.next; // cur 指向m的下一个节点
//3(pre) -> 4(cur) -> 5 -> NULL
while(length){ // 在m-n的范围内循环 改变节点指向 // 第二遍循环 只循环2次
let next = cur.next; // 3(pre) -> 4(cur) -> 5(next) -> NULL // 3 <- 4(pre) -> 5(cur) -> NULL
cur.next = pre; // 3(pre) <- 4(cur) -> 5(next) -> NULL // 3 <- 4(pre) -> 5(cur) -> NULL(next)
pre = cur; // 3 <- 4(pre) -> 5(cur) -> NULL // 3 <- 4(pre) <- 5(cur) -> NULL(next)
cur = next; // 3 <- 4 <- 5(pre) -> NULL(cur)
length --;
}
// 最后就变成了1 -> 2 -> 3 <- 4 <- 5 -> NULL
// 然后让 2.next(prior.next) 指向 5(pre)。 3.next(letter) 指向 NULL(cur)
prior.next = pre; // 也就是当时m的前一个节点指向反转完的pre(没反转前的n节点)
letter.next = cur;// 让m节点指向n后面的那个节点。
return newNode.next;
}
上面的代码结合图解,是不是更为清晰了呢?
;
以上就是我的思路以及解法了,希望大家喜欢我画的图解,我会继续努力的ヾ(◍°∇°◍)ノ゙。(点个赞把我带走吧~~)
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。