3

LeetCode 92.反转链表||

大家好,我是灵魂画师--茄子。技术水平一般,喜欢画画。

开始今天的正题。

说明:
1 ≤ m ≤ n ≤ 链表长度。

示例:

输入: 1->2->3->4->5->NULL, m = 2, n = 4
输出: 1->4->3->2->5->NULL

leetCode92题反转链表||

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;
}

上面的代码结合图解,是不是更为清晰了呢?

图解;

以上就是我的思路以及解法了,希望大家喜欢我画的图解,我会继续努力的ヾ(◍°∇°◍)ノ゙。(点个赞把我带走吧~~)


灵魂画师_茄子
11 声望4 粉丝