倒转时空,颠覆传统:探究逆置链表的三种思路

news2024/9/22 23:28:55

在这里插入图片描述
本篇博客会讲解力扣“206. 反转链表”的解题思路,这是题目链接。

老规矩,先来审题:
在这里插入图片描述

示例如下:
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
提示和进阶:
在这里插入图片描述
本题的思路非常多,我讲解一下常见的思路。

思路1

最容易想到的方法,是直接使用3个指针,把指向给倒过来。

首先定义prev和cur,分别代表前一个结点和遍历到的结点。

struct ListNode* reverseList(struct ListNode* head){
    struct ListNode* prev = NULL; // 前一个
    struct ListNode* cur = head; // 当前
}

接下来开始遍历。一开始先记录下一个结点,因为指向倒过来后就找不到下一个了。

struct ListNode* reverseList(struct ListNode* head){
    struct ListNode* prev = NULL; // 前一个
    struct ListNode* cur = head; // 当前
    while (cur)
    {
        struct ListNode* next = cur->next; // 下一个
    }
}

然后倒转指向。

struct ListNode* reverseList(struct ListNode* head){
    struct ListNode* prev = NULL; // 前一个
    struct ListNode* cur = head; // 当前
    while (cur)
    {
        struct ListNode* next = cur->next; // 下一个
        // 倒转指向
        cur->next = prev;
    }
}

接着迭代。

struct ListNode* reverseList(struct ListNode* head){
    struct ListNode* prev = NULL; // 前一个
    struct ListNode* cur = head; // 当前
    while (cur)
    {
        struct ListNode* next = cur->next; // 下一个
        // 倒转指向
        cur->next = prev;
        // 迭代
        prev = cur;
        cur = next;
    }
}

最后返回哪个结点呢?因为cur为空跳出循环,此时前一个结点,即prev,就是新的头结点。

struct ListNode* reverseList(struct ListNode* head){
    struct ListNode* prev = NULL; // 前一个
    struct ListNode* cur = head; // 当前
    while (cur)
    {
        struct ListNode* next = cur->next; // 下一个
        // 倒转指向
        cur->next = prev;
        // 迭代
        prev = cur;
        cur = next;
    }

    // cur为NULL,prev为新的头结点
    return prev;
}

在这里插入图片描述
这样就过了。

思路2

如果不想要像思路1那么直接,可以考虑遍历+头插。每次遍历到一个结点,就头插到新的链表,也能够实现逆置的效果。

struct ListNode* reverseList(struct ListNode* head){
    // 定义新的链表
    struct ListNode* newHead = NULL;
}

接着就是遍历+头插的逻辑。

struct ListNode* reverseList(struct ListNode* head){
    // 定义新的链表
    struct ListNode* newHead = NULL;
    // 遍历+头插
    struct ListNode* cur = head;
    while (cur)
    {
        // 头插
        cur->next = newHead;
        newHead = cur;
    }
}

接下来要迭代。但是此时cur->next已经改了,所以要先保存next。

struct ListNode* reverseList(struct ListNode* head){
    // 定义新的链表
    struct ListNode* newHead = NULL;
    // 遍历+头插
    struct ListNode* cur = head;
    while (cur)
    {
        struct ListNode* next = cur->next;
        // 头插
        cur->next = newHead;
        newHead = cur;
        // 迭代
        cur = next;
    }

    return newHead;
}

在这里插入图片描述
这样也能过。其实,本解法和第一种解法都用到了3个指针。而且,你仔细对比一下2个代码:

// 三指针强行倒转指向
struct ListNode* reverseList(struct ListNode* head){
    struct ListNode* prev = NULL; // 前一个
    struct ListNode* cur = head; // 当前
    while (cur)
    {
        struct ListNode* next = cur->next; // 下一个
        // 倒转指向
        cur->next = prev;
        // 迭代
        prev = cur;
        cur = next;
    }

    // cur为NULL,prev为新的头结点
    return prev;
}

// 遍历+头插
struct ListNode* reverseList(struct ListNode* head){
    // 定义新的链表
    struct ListNode* newHead = NULL;
    // 遍历+头插
    struct ListNode* cur = head;
    while (cur)
    {
        struct ListNode* next = cur->next;
        // 头插
        cur->next = newHead;
        newHead = cur;
        // 迭代
        cur = next;
    }

    return newHead;
}

把注释都去掉:

struct ListNode* reverseList(struct ListNode* head){
    struct ListNode* prev = NULL;
    struct ListNode* cur = head;
    while (cur)
    {
        struct ListNode* next = cur->next;
        cur->next = prev;
        prev = cur;
        cur = next;
    }
    return prev;
}

struct ListNode* reverseList(struct ListNode* head){
    struct ListNode* newHead = NULL;
    struct ListNode* cur = head;
    while (cur)
    {
        struct ListNode* next = cur->next;
        cur->next = newHead;
        newHead = cur;
        cur = next;
    }
    return newHead;
}

嘿,是不是一模一样!没错,就是变量名改了下,其他的都一样。所以,同一份代码,其实可以用不同的思路去理解。

思路3

本题还可以使用递归来解决。递归的本质是把大事化小,我们来分析一下:

  1. 可以递归调用自身,把除了头结点之外的结点逆置了。
  2. 接着把头结点链接到其余结点的最后面。

下面开始写代码:首先处理除了头结点之外的结点:

struct ListNode* reverseList(struct ListNode* head){
    // 处理头结点后面的所有结点
    struct ListNode* newHead = reverseList(head->next);
}

接着处理头结点,我们需要把头结点链接到其余结点的后面。注意:虽然其余结点都逆置了,但是头结点指向的结点并没有变化。此时,头结点指向的结点已经成为了其余结点的尾结点,所以直接head->next就能够找到其余结点的最后一个结点。

struct ListNode* reverseList(struct ListNode* head){
    // 处理头结点后面的所有结点
    struct ListNode* newHead = reverseList(head->next);
    // 把头结点链接到其余结点的后面
    head->next->next = head;
}

此时原来的头结点就成为了尾结点,但是它的next并不是NULL,所以需要置空。

struct ListNode* reverseList(struct ListNode* head){
    // 处理头结点后面的所有结点
    struct ListNode* newHead = reverseList(head->next);
    // 把头结点链接到其余结点的后面
    head->next->next = head;
    // 末尾置空
    head->next = NULL;

    return newHead;
}

当然,以上的操作是有2个以上的结点的场景,如果只有1个结点,甚至没有结点,就直接返回就行了。

struct ListNode* reverseList(struct ListNode* head){
    // 如果只有1个结点,或者没有结点,直接返回
    if (head == NULL || head->next == NULL)
        return head;
    
    // 处理头结点后面的所有结点
    struct ListNode* newHead = reverseList(head->next);
    // 把头结点链接到其余结点的后面
    head->next->next = head;
    // 末尾置空
    head->next = NULL;

    return newHead;
}

在这里插入图片描述
这样就通过了。其实我非常喜欢使用递归,虽然效率可能低了点,但是代码给人一种很舒服的感觉。

总结

  1. 迭代法:可以用三指针强行逆置,或者遍历+头插。2种思路最终写出来的代码是一样的。
  2. 递归法:还是那么好用,大部分链表的题目都可以考虑递归,这样可以快速写出代码,并且逻辑较为简洁。

感谢大家的阅读!

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

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

相关文章

macOS本地python环境/vscode/导入python包/设置python解释器

查看macbook本地是否有python环境 输入python或者python3,退出python环境使用exit(),别忘了括号 没有的话去官网安装https://www.python.org/ 2. 安装vscode 官网https://code.visualstudio.com/ 3. 安装插件 点击左边的“插件”按钮,安装…

springcloud-gateway集成knife4j(swagger3)

springcloud-gateway集成knife4j(swagger3) springcloud-gateway集成knife4j(swagger3) 环境信息准备工作微服务集成knife4j 第一步:编写Knife4jApiInfoProperties第二步:编写配置类Knife4jConfig第三步&a…

5月9号软件资讯更新合集......

Linux 嵌入式系统构建工具 Yocto 发布 4.2 版本 基于 Linux 基金会的 Yocto 项目发布了 4.2 版本。Yocto 提供模板、工具和方法,帮助开发者创建基于 Linux 的定制版物联网 / 嵌入式操作系统,而无需关心硬件体系。 4.2 中的新功能 / 增强功能 Linux 内核…

106.(cesium篇)cesium椎体旋转

听老人家说:多看美女会长寿 地图之家总目录(订阅之前建议先查看该博客) 文章末尾处提供保证可运行完整代码包,运行如有问题,可“私信”博主。 效果如下所示: 下面献上完整代码,代码重要位置会做相应解释 <html lang="en"> <

【简单入门】ChatGPT prompt engineering (中文版)笔记 |吴恩达ChatGPT 提示工程

目录 思维导图一、资料二、 指南环境配置两个基本原则&#xff08;最重要!!!!&#xff09;原则一&#xff1a;编写清晰、具体的指令**策略一&#xff1a;使用分隔符清晰地表示输入的不同部分**&#xff0c;**分隔符可以是&#xff1a;&#xff0c;""&#xff0c;<…

camunda的Java委托如何使用

一、camunda的Java委托有什么用途 在Camunda中&#xff0c;Java委托是一种用于在流程执行期间执行自定义逻辑的Java类。使用Java委托&#xff0c;您可以在流程执行期间通过Java代码实现各种复杂的业务逻辑。 以下是一些使用Java委托的常见用途&#xff1a; 1、计算值&#x…

算法设计 || 第3题:多边形算法+分治算法解决循环赛问题(奇偶赛)

2022北京冬奥会在新冠疫情肆虐的情况下仍然成功举办&#xff0c;体现了我们国家和组织者的集体智慧。假设有项体育比赛有N14个队伍需要和其他N-113只队伍进行循环赛, 如果偶数个队伍&#xff0c;每个队伍每天捉对赛一场&#xff0c;共进行N-113天比赛; 奇数个队伍会出现每天有…

【K8s】Pod一文详解

文章目录 一、Pod介绍1、Pod结构2、Pod的定义 二、Pod配置&#xff1a;spec.containers1、基本配置 name和image2、镜像拉取策略 imagePullpolicy3、启动命令 command4、环境变量 env5、端口设置 ports6、资源配额 resources 三、Pod的生命周期1、创建和终止2、初始化容器3、钩…

Docker 部署 Zabbix6.4

一、安装docker 1.1.离线安装docker docker网址&#xff1a;https://download.docker.com/linux/static/stable/x86_64/ [rootVM-16-15-centos ~]# mkdir docker_install [rootVM-16-15-centos ~]# cd docker_install/ [rootVM-16-15-centos docker_install]# vim docker.se…

使用proc文件系统

使用proc文件系统 文章目录 使用proc文件系统1.meminfo文件2. free命令3、创建 /proc 节点4、使用 file_operations 实现 proc 文件读写 导向内核信息5、使用 seq_file 实现 proc 文件的读取 在Linux系统中&#xff0c; “/proc”文件系统十分有用&#xff0c; 它被内核用于向用…

vue3学习四 watch

在vue3中使用watch 来监听某个数据的变化&#xff0c; 因为我们定义数据的时候有 ref 和 reactive 两种方法&#xff0c; 所以watch 也会分出不同的五种情况 当使用 watch 来监听 ref 定义的数据时 <template><div> sum: {{sum}}</div><button click&qu…

java非静态代码块和静态代码块介绍

代码块 SE.10.0…02.28 非静态普通代码块&#xff1a;定义在方法内部的代码块&#xff0c;不用任何关键字修饰&#xff0c;又名构造代码块、实例代码块 静态代码块&#xff1a;用static修饰的代码块 非静态代码块 public class Test {public static void main(String[] args…

【EKF】卡尔曼滤波的一维应用实例

前言 推导了卡尔曼滤波的原理之后&#xff0c;使用一个简单的一维应用实例来训练一下&#xff0c;加深印象。使用一个温度测量的实例来说明&#xff0c;系统的状态方程为&#xff1a; X(k) A*X(k-1) B*u(k-1) w(k-1) Z(k) H*X(k) v(k) 其中 w 为过程噪声&#xff0c;方…

方言翻译APP小程序开发具备哪些功能?

我国语言文华博大精深&#xff0c;很多地方都有着民族特色方言&#xff0c;在当地很盛行但是外地人听不懂也不会说&#xff0c;这就給沟通造成了一定的困扰。方言翻译APP软件是专门针对地方性方言开发的一款系统软件&#xff0c;提供全国各地方言翻译功能&#xff0c;一键在线就…

Android中如何使用GPS

目录 GPS简介 GPS的常用API locationProvider 使用GPS获取位置信息 室内WIFI定位 近距离警报 GPS简介 Gobal Positioning System&#xff0c;全球定位系统&#xff0c;是美国在20世纪70年代研制的一种以人造地球卫星为基础的高精度无线电导航的定位系统&#xff0c;它在全…

【手机摄影】--全集

算了&#xff0c;摄像机太贵了&#xff0c;玩不起&#xff0c;还是看看手机摄影吧。 学习链接 &#xff1a;https://www.bilibili.com/video/BV14e411T7md 1. 认识手机摄影 1.1 局限性 手机的摄像头能够满足大部分场景&#xff0c;但以下场景会受到掣肘&#xff0c;最好还是用…

面试这么简单,阿里原来这么容易就能进去…...

最近和阿里的一个老朋友闲聊&#xff0c;感触颇深&#xff0c;据他说公司近期招聘的测试工程师&#xff0c;大多数候选人都有一个“通病”&#xff1a;在工作2-3年的时候遇到瓶颈&#xff0c;而且是一道很难跨越的坎。 为什么会遇到这种情况&#xff1f;因为大部分测试工程师在…

项目管理基础(第五版)读书笔记

项目管理基础&#xff08;第五版&#xff09;读书笔记 章节概要前言第一章&#xff1a;项目管理概述 日期&#xff1a;2023年3月23日 章节概要 前言 项目管理协会 Project Management Institute。简称PMI。项目管理知识体系指南 Project Management Body Of Knowledge。简称P…

4.QT应用程序主窗口

本章代码见文末链接 主窗口框架 新建Qt Wisgets项目mymainwindow&#xff0c;类名默认MainWindow&#xff0c;基类默认QMainWindow 更改文字如图&#xff0c;如果中文无法直接输入&#xff0c;可以试试复制粘贴 “动作编辑器”中&#xff08;默认在右下角&#xff09;&…

关于IRIG-B码对时的理解和分析

一、IRIG-B是什么&#xff1f; IRIG-B&#xff08;简称B码&#xff09;是一种应用于靶场的串行时间交换码。由美国靶场司令部委员会下属“靶场仪器组”提出的一种时间信息编码标准&#xff08;IRIG是英文InterRange Instrumentation Group的缩写。它是美国靶场司令委员会的下属…