【数据结构和算法】反转链表

其他系列文章导航

Java基础合集
数据结构与算法合集

设计模式合集

多线程合集

分布式合集

ES合集


文章目录

其他系列文章导航

文章目录

前言

一、题目描述

二、题解

[2.1 方法一:迭代(双指针)](#2.1 方法一:迭代(双指针))

[2.2 方法二:递归](#2.2 方法二:递归)

三、代码

[3.1 方法一:迭代(双指针)](#3.1 方法一:迭代(双指针))

[3.2 方法二:递归](#3.2 方法二:递归)

四、复杂度分析

[4.1 方法一:迭代(双指针)](#4.1 方法一:迭代(双指针))

[4.2 方法二:递归](#4.2 方法二:递归)


前言

这是力扣的 206 题,难度为简单,解题方案有很多种,本文讲解我认为最奇妙的一种。

继续开始链表的模块了,这道题是一道非常好的队列的例题,很有代表性。


一、题目描述

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

示例 1:

复制代码
输入:head = [1,2,3,4,5]
输出:[5,4,3,2,1]

示例 2:

复制代码
输入:head = [1,2]
输出:[2,1]

示例 3:

复制代码
输入:head = []
输出:[]

提示:

  • 链表中节点的数目范围是 [0, 5000]
  • -5000 <= Node.val <= 5000

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


二、题解

因为进阶要求两种方法来解决这道题目,所以本文都讲解!

如下图所示,题目要求将链表反转。本文介绍迭代(双指针)、递归两种实现方法。

2.1 方法一:迭代(双指针)

思路与算法:

假设链表为 1→2→3→∅,我们想要把它改成 ∅←1←2←3。

在遍历链表时,将当前节点的 next 指针改为指向前一个节点。由于节点没有引用其前一个节点,因此必须事先存储其前一个节点。在更改引用之前,还需要存储后一个节点。最后返回新的头引用。

2.2 方法二:递归

递归版本稍微复杂一些,其关键在于反向工作。假设链表的其余部分已经被反转,现在应该如何反转它前面的部分?

假设链表为:

n1→...→nk−1→nk→nk+1→...→nm→∅

若从节点 nk+1到 nm已经被反转,而我们正处于 nk。

n1→...→nk−1→nk→nk+1←...←nm

我们希望 nk+1的下一个节点指向 nk。

所以,nk.next.next=nk

需要注意的是 n1的下一个节点必须指向 ∅。如果忽略了这一点,链表中可能会产生环。


三、代码

3.1 方法一:迭代(双指针)

Java版本:

java 复制代码
class Solution {
    public ListNode reverseList(ListNode head) {
        ListNode cur = head, pre = null;
        while(cur != null) {
            ListNode tmp = cur.next; // 暂存后继节点 cur.next
            cur.next = pre;          // 修改 next 引用指向
            pre = cur;               // pre 暂存 cur
            cur = tmp;               // cur 访问下一节点
        }
        return pre;
    }
}

C++版本:

cpp 复制代码
class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        ListNode *cur = head, *pre = nullptr;
        while(cur != nullptr) {
            ListNode* tmp = cur->next; // 暂存后继节点 cur.next
            cur->next = pre;           // 修改 next 引用指向
            pre = cur;                 // pre 暂存 cur
            cur = tmp;                 // cur 访问下一节点
        }
        return pre;
    }
};

Python版本:

python 复制代码
class Solution:
    def reverseList(self, head: ListNode) -> ListNode:
        cur, pre = head, None
        while cur:
            tmp = cur.next # 暂存后继节点 cur.next
            cur.next = pre # 修改 next 引用指向
            pre = cur      # pre 暂存 cur
            cur = tmp      # cur 访问下一节点
        return pre

3.2 方法二:递归

Java版本:

java 复制代码
 public ListNode reverseList(ListNode head) {
        return recur(head, null);
    }

    private ListNode recur(ListNode cur, ListNode pre) {
        if (cur == null) return pre;
        ListNode res = recur(cur.next, cur);
        cur.next = pre;
        return res;
    }

C++版本:

cpp 复制代码
class ListNode {
public: 
    int val;
    ListNode* next;
};

ListNode* reverseList(ListNode* head) {
    ListNode* pre = nullptr;
    ListNode* cur = head;
    while (cur) {
        ListNode* next = cur->next;
        cur->next = pre;
        pre = cur;
        cur = next;
    }
    return pre;
}

Python版本:

python 复制代码
class ListNode:
    def __init__(self, val=0, next=None):
        self.val = val
        self.next = next

def reverseList(head):
    def recur(cur, pre):
        if not cur:
            return pre
        res = recur(cur.next, cur)
        cur.next = pre
        return res

    return recur(head, None)

四、复杂度分析

4.1 方法一:迭代(双指针)

  • 时间复杂度 O(N) : 遍历链表使用线性大小时间。
  • 空间复杂度 O(1) : 变量 pre 和 cur 使用常数大小额外空间。

4.2 方法二:递归

  • 时间复杂度 O(N) : 遍历链表使用线性大小时间。
  • 空间复杂度 O(N) : 遍历链表的递归深度达到 N ,系统使用 O(N) 大小额外空间。

相关推荐
卡尔特斯4 小时前
Android Kotlin 项目代理配置【详细步骤(可选)】
android·java·kotlin
白鲸开源4 小时前
Ubuntu 22 下 DolphinScheduler 3.x 伪集群部署实录
java·ubuntu·开源
ytadpole4 小时前
Java 25 新特性 更简洁、更高效、更现代
java·后端
纪莫4 小时前
A公司一面:类加载的过程是怎么样的? 双亲委派的优点和缺点? 产生fullGC的情况有哪些? spring的动态代理有哪些?区别是什么? 如何排查CPU使用率过高?
java·java面试⑧股
JavaGuide5 小时前
JDK 25(长期支持版) 发布,新特性解读!
java·后端
用户3721574261355 小时前
Java 轻松批量替换 Word 文档文字内容
java
白鲸开源5 小时前
教你数分钟内创建并运行一个 DolphinScheduler Workflow!
java
CoovallyAIHub6 小时前
中科大DSAI Lab团队多篇论文入选ICCV 2025,推动三维视觉与泛化感知技术突破
深度学习·算法·计算机视觉
Java中文社群6 小时前
有点意思!Java8后最有用新特性排行榜!
java·后端·面试
代码匠心6 小时前
从零开始学Flink:数据源
java·大数据·后端·flink