【链表】leetcode206.反转链表(C/C++/Java/Js)

news2025/1/21 0:53:00

leetcode206.反转链表

  • 1 题目
  • 2 思路
    • 2.1 双指针法(迭代)
    • 2.2 递归法
      • 2.2.1 递归--从前往后翻转指针指向
      • 2.2.2 递归--从后往前翻转指针指向
  • 3 代码
    • 3.1 C++版本(迭代)
    • 3.2 C版本(迭代+递归)
    • 3.3 Java版本(迭代+递归)
    • 3.4 JavaScript版本(迭代+递归)
  • 4 总结


1 题目

题源链接

给你单链表的头节点 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 思路

2.1 双指针法(迭代)

在这里插入图片描述

首先定义一个cur指针,指向头结点,再定义一个pre指针,初始化为null。

然后就要开始反转了,首先要把 cur->next 节点用tmp指针保存一下,也就是保存一下这个节点。

为什么要保存一下这个节点呢,因为接下来要改变 cur->next 的指向了,将cur->next 指向pre ,此时已经反转了第一个节点了。

接下来,就是循环走如下代码逻辑了,继续移动pre和cur指针。

最后,cur 指针已经指向了null,循环结束,链表也反转完毕了。 此时我们return pre指针就可以了,pre指针就指向了新的头结点。


2.2 递归法

递归法相对抽象一些,但是其实和双指针法是一样的逻辑,同样是当cur为空的时候循环结束,不断将cur指向pre的过程。

关键是初始化的地方,可能有的同学会不理解, 可以看到双指针法中初始化 cur = head,pre = NULL,在递归法中可以从如下代码看出初始化的逻辑也是一样的,只不过写法变了。

具体可以看代码(已经详细注释),双指针法写出来之后,理解如下递归写法就不难了,代码逻辑都是一样的。


2.2.1 递归–从前往后翻转指针指向

class Solution {
public:
    ListNode* reverse(ListNode* pre,ListNode* cur){
        if(cur == NULL) return pre;
        ListNode* temp = cur->next;
        cur->next = pre;
        // 可以和双指针法的代码进行对比,如下递归的写法,其实就是做了这两步
        // pre = cur;
        // cur = temp;
        return reverse(cur,temp);
    }
    ListNode* reverseList(ListNode* head) {
        // 和双指针法初始化是一样的逻辑
        // ListNode* cur = head;
        // ListNode* pre = NULL;
        return reverse(NULL, head);
    }

};

2.2.2 递归–从后往前翻转指针指向

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        // 边缘条件判断
        if(head == NULL) return NULL;
        if (head->next == NULL) return head;
        
        // 递归调用,翻转第二个节点开始往后的链表
        ListNode *last = reverseList(head->next);
        // 翻转头节点与第二个节点的指向
        head->next->next = head;
        // 此时的 head 节点为尾节点,next 需要指向 NULL
        head->next = NULL;
        return last;
    }
}; 

不理解的话,画图模拟一下。


3 代码

3.1 C++版本(迭代)

class Solution {
public:
    ListNode* reverseList(ListNode* head) {
        ListNode* temp; // 保存cur的下一个节点
        ListNode* cur = head;
        ListNode* pre = NULL;
        while(cur) {
            temp = cur->next;  // 保存一下 cur的下一个节点,因为接下来要改变cur->next
            cur->next = pre; // 翻转操作
            // 更新pre 和 cur指针
            pre = cur;
            cur = temp;
        }
        return pre;
    }
};

3.2 C版本(迭代+递归)

迭代:

struct ListNode* reverseList(struct ListNode* head){
    //保存cur的下一个结点
    struct ListNode* temp;
    //pre指针指向前一个当前结点的前一个结点
    struct ListNode* pre = NULL;
    //用head代替cur,也可以再定义一个cur结点指向head。
    while(head) {
        //保存下一个结点的位置
        temp = head->next;
        //翻转操作
        head->next = pre;
        //更新结点
        pre = head;
        head = temp;
    }
    return pre;
}

递归:

struct ListNode* reverse(struct ListNode* pre, struct ListNode* cur) {
    if(!cur)
        return pre;
    struct ListNode* temp = cur->next;
    cur->next = pre;
    //将cur作为pre传入下一层
    //将temp作为cur传入下一层,改变其指针指向当前cur
    return reverse(cur, temp);
}

struct ListNode* reverseList(struct ListNode* head){
    return reverse(NULL, head);
}

3.3 Java版本(迭代+递归)

迭代:

// 双指针
class Solution {
    public ListNode reverseList(ListNode head) {
        ListNode prev = null;
        ListNode cur = head;
        ListNode temp = null;
        while (cur != null) {
            temp = cur.next;// 保存下一个节点
            cur.next = prev;
            prev = cur;
            cur = temp;
        }
        return prev;
    }
}

递归(从前向后):

// 递归 
class Solution {
    public ListNode reverseList(ListNode head) {
        return reverse(null, head);
    }

    private ListNode reverse(ListNode prev, ListNode cur) {
        if (cur == null) {
            return prev;
        }
        ListNode temp = null;
        temp = cur.next;// 先保存下一个节点
        cur.next = prev;// 反转
        // 更新prev、cur位置
        // prev = cur;
        // cur = temp;
        return reverse(cur, temp);
    }
}

从后向前递归:

// 从后向前递归
class Solution {
    ListNode reverseList(ListNode head) {
        // 边缘条件判断
        if(head == null) return null;
        if (head.next == null) return head;
        
        // 递归调用,翻转第二个节点开始往后的链表
        ListNode last = reverseList(head.next);
        // 翻转头节点与第二个节点的指向
        head.next.next = head;
        // 此时的 head 节点为尾节点,next 需要指向 NULL
        head.next = null;
        return last;
    } 
}

3.4 JavaScript版本(迭代+递归)

/**
 * @param {ListNode} head
 * @return {ListNode}
 */

// 双指针:
var reverseList = function(head) {
    if(!head || !head.next) return head;
    let temp = null, pre = null, cur = head;
    while(cur) {
        temp = cur.next;
        cur.next = pre;
        pre = cur;
        cur = temp;
    }
    // temp = cur = null;
    return pre;
};

递归:从前往后

/**
 * @param {ListNode} head
 * @return {ListNode}
 */
 // 递归:
var reverse = function(pre, head) {
    if(!head) return pre;
    const temp = head.next;
    head.next = pre;
    pre = head
    return reverse(pre, temp);
}

var reverseList = function(head) {
    return reverse(null, head);
};

递归:从后往前

/**
 * @param {ListNode} head
 * @return {ListNode}
 */
// 递归2
var reverse = function(head) {
    if(!head || !head.next) return head;
    // 从后往前翻
    const pre = reverse(head.next);
    head.next = pre.next;
    pre.next = head;
    return head;
}

var reverseList = function(head) {
    let cur = head;
    while(cur && cur.next) {
        cur = cur.next;
    }
    reverse(head);
    return cur;
};

4 总结

当时备考数据结构和算法的时候,这类题经常会出现。
重点是理解
理解什么呢?理解链表的结构,缺什么设什么。双指针法比较直接也比较好理解。
递归稍有困难,但如果理解过程了也是很容易实现的。
如果有困难可以去看帮你拿下反转链表有助于对本题更好的理解。

我在看一些自命题学校的往年真题中,有的题目会要求你用递归/迭代的方式完成,所以我认为两种方式最好都掌握。

对后面二叉树章节的学习也有帮助,因为二叉树题目当中几乎都是递归,真正理解到了递归的奥妙的时候,你会发现做题非常爽!

By --Suki 2023/1/9

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/151987.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【OpenDDS开发指南V3.20】第十章:Java Bindings

介绍 OpenDDS提供JNI绑定。Java应用程序可以像C++应用程序一样使用完整的OpenDDS中间件。 有关入门的信息,包括先决条件和依赖项,请参阅$DDS_ROOT/java/INSTALL文件。 Java版本9和更高版本使用Java平台模块系统。要在这些Java版本中使用OpenDDS,请将MPC特性Java_pre_jpms…

QT Echarts 联动共享数据表图 使用详解

Echarts是百度的一款可视化界面开发的平台&#xff0c;里面的地图以及数据可视化内容十分丰富&#xff0c;适合用于一些大屏数据显示项目或者一些ui界面开发。每一个ECharts图表使用一个无边框的QWebView来展示&#xff0c;这样多个不同类型的ECharts图表就是多个封装不同类型E…

kettle 筛选数据 并根据关键字段去重 设计

文章目录前言kettle 筛选数据 并根据关键字段去重 设计实现:1、配置sqlite 数据库链接2、先从test表里抽取数据3、将表输入查询的数据插入到excel里4、将筛选出来的数据根据id去重5、插入本地excel6、ETL 整体效果:7、测试:前言 如果您觉得有用的话&#xff0c;记得给博主点个赞…

安全轻量化股票看盘盯盘软件需要实现的功能和基本要求是什么?

有很多投资者是上班族的&#xff0c;因此是不能无时无刻盯盘看盘的&#xff0c;那么为了解决这个问题就需要用上轻量化股票看盘盯盘软件&#xff0c;那么一个安全的轻量化股票看盘盯盘软件需要具备哪些功能和基本要求呢&#xff1f;接下来小编为大家分析分析&#xff01; 1.一定…

小试跨平台局域网文件传输工具NitroShare,几点感想

随着电脑系统国产化的推进&#xff0c;单位用的OA系统已转移到国产电脑上了&#xff0c;但是国产电脑上的操作系统基于Linux&#xff0c;软件商店里可选的应用软件还不够多&#xff0c;功能也还有待提高。为了提高处理效率&#xff0c;经常需要把文件从国产电脑传到Windows平台…

信息收集过程WAF绕过详解

今天继续给大家介绍渗透测试相关知识&#xff0c;本文主要内容是信息收集过程WAF绕过详解。 免责声明&#xff1a; 本文所介绍的内容仅做学习交流使用&#xff0c;严禁利用文中技术进行非法行为&#xff0c;否则造成一切严重后果自负&#xff01; 再次强调&#xff1a;严禁对未…

浅析数据中心机架配电母线的应用及监控

摘要&#xff1a;本文先分析配电母线槽创新点和优势&#xff0c;然后结合湛江数据中心302机房母线槽建设对配电母线槽和列头柜两种供电方式进行经济效益对比&#xff0c;最后总结推广应用建议&#xff0c;以期为相关工程技术人员提供参考。 关键词&#xff1a;机架配电母线&a…

【动态路由和导航守卫】一.动态路由;二.路由中的查询参数;三.命名路由;四.命名视图;五.声明式导航 编程式导航;六.导航守卫

目录 一.动态路由 1.什么是动态路由&#xff1f; 2.动态路由如何进行参数的传递&#xff1a; &#xff08;1&#xff09;如何设置URL地址中的参数&#xff1a;/url/:参数名 &#xff08;2&#xff09;在组件中接收参数&#xff1a;this.$route.params.参数名 3.$route和$r…

最新版android-studio无法安装Lombok插件?魔改后可任意安装版本教程(附已魔改下载地址)

&#x1f935;‍♂️ 个人主页: 奇怪的守护神 &#x1f468;‍&#x1f4bb; 作者简介&#xff1a;十年全栈开发经验&#xff0c;团队负责人。喜欢钻研技术&#xff0c;争取成为编程达人 &#x1f396;️&#xff01; &#x1f5fa;️学海无涯苦作舟&#xff0c;&#x1f6e4;️…

【自学Python】Python字符串(string)

Python字符串(string) Python字符串(string)教程 字符串是一个不可改变的字节序列。字符串可以包含任意的数据&#xff0c;但是通常是用来包含可读的文本。 Python字符串(string) Python 字符串定义有五种形式&#xff0c;使用单引号、双引号、三个单引号 、三个双引号以及…

胡凡 《算法笔记》 上机实战训练指南 chap3 入门模拟: 3.2 查找元素

胡凡 《算法笔记》 上机实战训练指南 chap3 入门模拟: 3.2 查找元素 文章目录胡凡 《算法笔记》 上机实战训练指南 chap3 入门模拟: 3.2 查找元素【PAT B1041】考试座位号【PAT B1004】成绩排名【PAT B1028】人口普查解决过程(cpp)AC代码python实现AC代码pycode1pycode2未AC代码…

代码随想录算法训练营第7天 383.赎金信、454. 四数相加II、15.三数之和、18.四数之和

代码随想录算法训练营第7天 383.赎金信、454. 四数相加II、15.三数之和、18.四数之和 赎金信 力扣题目链接(opens new window) 给定一个赎金信 (ransom) 字符串和一个杂志(magazine)字符串&#xff0c;判断第一个字符串 ransom 能不能由第二个字符串 magazines 里面的字符构…

ERP系统到底能做什么?

ERP是什么&#xff1f;ERP即企业资源计划&#xff0c;ERP系统主要是优化企业内部的业务流程&#xff0c;用信息化管控的方式进行采购管理、库存管理、销售管理和财务管理等板块。它可以看作是进销存系统的进阶版&#xff0c;主要针对供应链中下游。 一、ERP系统怎么产生的&…

SpringBoot(项目创建使用+配置文件+日志文件)

目录 1. Spring Boot 项目创建 2. 写一个 Hello World 并运行 3. 配置文件的作用及格式 4. properties 配置文件的基本语法 5. 读取配置文件 6. yml 配置文件说明 7. properties 和 yml 的区别 8. SpringBoot 日志文件 8.1 日志的作用 8.2 自定义日志打印 8.3 日志…

低代码:全力构筑企业数字转型新生态

数字转型企业处于数字经济大潮的风口浪尖&#xff0c;既是创新主体也是数字技术广泛运用的重要平台&#xff0c;主动调整企业发展战略以顺应数字化转型是其明智抉择。企业经营决策者应深刻认识数字化转型的发展特点及本质要求&#xff0c;看到数字化转型是企业战略的迭代升级&a…

C#,图像二值化(19)——全局阈值的香巴拉算法( Shanbhag Thresholding)及源程序

1 算法描述&#xff08;凑数&#xff09;thresholdShanbhag由Robert Haase基于G.Landini和W.Rasband的工作。自动阈值器利用ImageJ中实现的Shanbhag阈值方法&#xff0c;使用GPU上确定的直方图创建尽可能类似于ImageJ“应用阈值”方法的二进制图像。thresholdShanbhag By Rober…

「精致店主理人」:青年敢有所为,梦想掷地有声

第三期「精致店主理人」青年创业孵化营于12月16日在周大福顺德匠心智造中心&#xff0c;完美收官&#xff01;「精致店主理人」青年创业孵化营是在共青团深圳市委员会的指导下&#xff0c;由深圳市青少年发展基金会与周大福珠宝集团联合主办&#xff0c;郑家纯青年发展专项基金…

CPU基本结构和运行原理

1 CPU的基本结构 1.1 CPU是一个计算系统的核心 Control Unit&#xff0c;负责控制。如指令计数器&#xff0c;指令跳转。 Logic Unit&#xff0c;负责计算。如加减&#xff0c;比较大小等。 1.2 南北桥芯片将CPU与外设连接 北桥&#xff1a;CPU和内存、显卡等部件进行数据交…

Python解题 - CSDN周赛第22期 - 取数字

又是大放水的一期&#xff0c;连我都可以10分钟解决战斗了。得益于Python&#xff0c;前面三题5分钟内就pass了&#xff0c;而最后一题也是之前刷过类似的。。。于是相应地&#xff0c;这期的题解也会简短一些。 这次的好成绩代表不了实力&#xff0c;但也希望这样的好运气能一…

自然语言处理 概览理解 NLP specialization - Supervised ML

自然语言处理 概览理解 NLP specialization - Supervised ML Remember that understanding the data is one of the most critical steps in Data Science 自然语言处理可以实现语义识别&#xff0c;情感识别&#xff0c;文本翻译等等功能&#xff0c;当然最近情况下最火的便…