反转链表——递归,链表

October 24, 2021

题目:

给你单链表的头节点 head ,请你反转链表,并返回反转后的链表。

示例:

示例一:

示例一图片

输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]

示例二:

示例二图片

输入:head = [1,2]
输出:[2,1]

示例三:

输入:head = []
输出:[]

提示:

链表中节点的数目范围是 [0, 5000]

-5000 <= Node.val <= 5000

进阶:

链表可以选用迭代或递归方式完成反转。你能否用两种方法解决这道题?

思路

这道题的关键就是如何处理节点的 next 指针。

该问题是一个关于链表问题,先要将完整的问题拆分成子问题,自然想到以链表的节点作为子问题的单元。我们聚焦到链表中的单个节点,发现只要将每个节点的 next 指针指向前一个节点就可以实现链表的反转了。所以处理每一个节点时,需要用到该节点的前一个节点

迭代实现

迭代的思路比较常规,保存两个指针,一个 cur 指针指向当前遍历的节点,一个 prev 指针指向当前遍历的节点的前一个节点,对链表遍历一遍,将每个节点的 next 指针指向 prev

递归实现

递归的思路通常不会第一时间被想到,注意这个方法存在调用栈超过最大限制的风险。思路就是利用函数调用栈先进后出的结构特点,函数调用时栈帧压入栈的顺序,与函数返回时栈帧弹出栈的顺序正好相反。所以如果按照原来的顺序对每个节点依次调用递归函数,那么在函数的返回阶段,相当于按照相反的顺序访问链表,这样每个函数中,递归调用的返回值就正好是当前节点的上一个节点。这样相当于隐式保存了当前节点和上一个节点,但是这样在调用时会在调用栈保存链表中的所有节点,所以内存占用会比较大。

注意

  • 递归实现中,注意要保存递归调用阶段的最后一个节点,这个节点是反转后的链表的首节点,也是该问题的返回值。
  • 反转前的第一个节点,反转过后,这个节点就变成了最后一个节点,所以需要将这个节点的 next 赋值为 null,否则链表遍历一轮之后链表中会出现环,导致无法跳出循环或者递归过程。

代码

公共部分

/**
 * 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)
 *     }
 * }
 */

迭代实现

function reverseList(head: ListNode | null): ListNode | null {
    if(!head || head.next === null){
        return head;
    }
    let [prev, cur] = [head, head.next];
    head.next = null;
    while(cur){
        const next = cur.next;
        cur.next = prev;
        prev = cur;
        cur = next;
    }
    return prev;
};

递归实现

function reverseList(head: ListNode | null): ListNode | null {
    if(!head) return head;
    let newHead = head;
    
    function reverseListCore(head: ListNode | null): ListNode | null {
        if (head.next !== null) {
            const reversedListTail = reverseListCore(head.next);
            reversedListTail.next = head;
        } else {
            newHead = head; // 保存最后一个节点作为反转后链表的第一个节点
        } 
        return head;
    }
    
    reverseListCore(head);
    head.next = null; // 防止出现环
    return newHead;
};

相关阅读


Profile picture

这里是要没时间了

职业是前端开发,早餐喜欢吃 711 的芝士猪排饭团。喜欢写代码,爱折腾

© 2021, 分享知识和生活,记录成长与感动。