每日一题---OJ题: 合并两个有序链表

嗨!小伙伴们,好久不见啦! 今天我们来看看一道很有意思的一道题---合并两个有序链表

嗯,题目看上去好像不难,我们一起画图分析分析吧!

上图中,list1有3个结点,分别为1,2,3 ; list2中有3个结点,分别为1,3,4, 题目要求我们要将这两个链表合并到一起,并且是升序,最后将链表返回。

思路1:定义2个变量,l1和l2,分别指向list1和list2,它们从头遍历链表,依次比较每个结点的数据域,如果l1指向的结点的数据域小于l2指向的结点的数据域,那么就将l1尾插到新链表中; 反之亦然。

当 l1 && l2 不为空的时候,进入while循环(注意是 &&,因为只要有一个不满足条件,就可以跳出while循环),判断第一个结点,判断完毕后,尾插到新链表中(如果新链表为空,那么这个结点就是链表的头结点和尾结点 ; 如果链表不为空,这个结点就是链表的新的尾结点)。

第一次:  将list2中第一个结点尾插到新链表中,该结点就是链表的头结点和尾结点。l2继续往后遍历链表。

第二次:l1指向的结点的数据域小于l2,因此将该结点尾插到新链表中,这个结点就是链表的新的尾结点,l1指向下一个结点。

第三次:l1指向的结点的数据域小于l2,因此将该结点尾插到新链表中,这个结点就是链表的新的尾结点,l1指向下一个结点。

第四次: l2指向的结点的数据域小于l1,因此将该结点尾插到新链表中,这个结点就是链表的新的尾结点,l2指向下一个结点。

第五次:

第六次:

好啦,大致思路就是这样,接下来我们开始实现吧!

首先,定义2个变量l1和l2,分别指向list1和list2; 其次,我们定义新链表的表头newHead和表尾newTail。

struct ListNode* l1 = list1;                    //定义l1变量,指向list1
struct ListNode* l2 = list2;                    //定义l2变量,指向list2

struct ListNode* newHead = NULL;                //定义新链表的头结点
struct ListNode* newTail = NULL;                //定义新链表的尾结点

当  l1 && l2不为空,进入循环,依次比较每一个结点。

 while( l1 && l2){
    if(l1->val < l2->val){
          //l1比l2小
    if(newTail == NULL){
         //如果链表为空
        newHead = newTail = l1;
     }else{
        //链表不为空
        newTail->next = l1;
        newTail = l1;
      }
    l1 = l1->next;        //l1指向下一个结点

   }else{
        //l2比l1小
    if(newTail == NULL){
        //如果链表为空
        newHead = newTail = l2;
       }else{
        //链表不为空
        newTail->next = l2;
        newTail = l2;
      }
    l2 = l2->next;        //l2指向下一个结点
   }
}

是不是这里就结束了呢? 当然不是! 当 l1 走到NULL的位置 或者 l2 走到空的位置, 会跳出循环,来到后面的代码。

当 l1还未遍历完链表时,我们只需要将 newTail的next指针指向 l1 就可以啦! 用 if条件来做判断,本身就只让它执行一次,因为链表最后剩下的,就直接往后挂了,链表的每一个结点是通过next指针链接的,找到一个结点,就可以找到后续的所有结点。

(就像老鹰捉小鸡一样,是不是从中间折断的话,那后面的那些人还是连在一起的,重新连接的话,那只用把后半段的第一个人找到,挂回去,那后面的人也就一并连接回去了。)

if(l1){
        //l1没有遍历完链表
        newTail->next = l1;
    }
    if(l2){
        //l2没有遍历完链表
        newTail->next = l2;
    }

好啦,最后我们返回newHead就可以啦! 但是还有一个小小的问题需要注意: 传过来的参数 list1 或者 list2 有可能为空, 因此我们需要在开始就判断一下它们是否为空。

 if(list1 == NULL){
        //如果list1为空,则返回list2
        return list2;
    }

    if(list2 == NULL){
        //如果list2为空,则返回list1
        return list1;
    }

OK,整体代码如下: 

 typedef struct ListNode ListNode;
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) {
    if(list1 == NULL){
        //如果list1为空,则返回list2
        return list2;
    }

    if(list2 == NULL){
        //如果list2为空,则返回list1
        return list1;
    }

    ListNode* l1 = list1;//定义l1变量,指向list1
    ListNode* l2 = list2; //定义l2变量,指向list2

    ListNode* newHead = NULL; //定义新链表的头结点
    ListNode* newTail = NULL; //定义新链表的尾结点

 while( l1 && l2){
    if(l1->val < l2->val){
          //l1比l2小
    if(newTail == NULL){
         //如果链表为空
        newHead = newTail = l1;
     }else{
        //链表不为空
        newTail->next = l1;
        newTail = l1;
      }
    l1 = l1->next;        //l1指向下一个结点

   }else{
        //l2比l1小
    if(newTail == NULL){
        //如果链表为空
        newHead = newTail = l2;
       }else{
        //链表不为空
        newTail->next = l2;
        newTail = l2;
      }
    l2 = l2->next;      //l2指向下一个结点
   }
}

    if(l1){
        //l1没有遍历完链表
        newTail->next = l1;
    }
    if(l2){
        //l2没有遍历完链表
        newTail->next = l2;
    }
    return newHead; //返回头结点
}

好的,这道题我们基本上做完了,可是,看看这代码,好像有重复冗余的部分,我们如何优化代码呢? 

答案是: 我们可以定义一个哨兵结点,这个结点可以不存放数据,让它指向新链表的头结点。 

    ListNode* node =(ListNode*) malloc(sizeof(ListNode));   //创建一个哨兵结点
    ListNode* newHead = node;                               //头结点指向哨兵结点
    ListNode* newTail = node;                               //尾结点指向哨兵结点

中间的循环也需要更改,不用判断链表是否为空了。

 while( l1 && l2){
    if(l1->val < l2->val){
    //       //l1比l2小
    // if(newTail == NULL){
    //      //如果链表为空
    //     newHead = newTail = l1;
    //  }else{
    //     //链表不为空
    //     newTail->next = l1;
    //     newTail = l1;
    //  }
        newTail->next = l1;
        newTail = l1;
        l1 = l1->next;        //l1指向下一个结点
      }else{
        //l2比l1小
    // if(newTail == NULL){
    //     //如果链表为空
    //     newHead = newTail = l2;
    //    }else{
    //     //链表不为空
    //      newTail->next = l2;
    //      newTail = l2;
        newTail->next = l2;
        newTail = l2;
        l2 = l2->next;      //l2指向下一个结点
   }
}

malloc了空间,但这块空间实际上用不了,最后我们需要将哨兵结点释放

    //malloc了空间,但这块空间实际上用不了,应该将其释放掉
    ListNode* ret = newHead->next;
    free(newHead);
    return ret; //返回头结点的下一个结点

好啦,优化过的代码如下:

 typedef struct ListNode ListNode;
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) {
    if(list1 == NULL){
        //如果list1为空,则返回list2
        return list2;
    }

    if(list2 == NULL){
        //如果list2为空,则返回list1
        return list1;
    }

    ListNode* l1 = list1;//定义l1变量,指向list1
    ListNode* l2 = list2; //定义l2变量,指向list2

    ListNode* node =(ListNode*) malloc(sizeof(ListNode));   //创建一个哨兵结点
    ListNode* newHead = node;                               //头结点指向哨兵结点
    ListNode* newTail = node;                               //尾结点指向哨兵结点

 while( l1 && l2){
    if(l1->val < l2->val){
    //       //l1比l2小
    // if(newTail == NULL){
    //      //如果链表为空
    //     newHead = newTail = l1;
    //  }else{
    //     //链表不为空
    //     newTail->next = l1;
    //     newTail = l1;
    //  }
        newTail->next = l1;
        newTail = l1;
        l1 = l1->next;        //l1指向下一个结点
      }else{
        //l2比l1小
    // if(newTail == NULL){
    //     //如果链表为空
    //     newHead = newTail = l2;
    //    }else{
    //     //链表不为空
    //      newTail->next = l2;
    //      newTail = l2;
        newTail->next = l2;
        newTail = l2;
        l2 = l2->next;      //l2指向下一个结点
   }
}

//跳出循环存在两种情况,要么l1走到空l2不为空,要么l2走到空,l1不为空
//不可能存在都为空的情况

    if(l1){
        //l1没有遍历完链表
        newTail->next = l1;
    }
    if(l2){
        //l2没有遍历完链表
        newTail->next = l2;
    }

    //malloc了空间,但这块空间实际上用不了,应该将其释放掉
    ListNode* ret = newHead->next;
    free(newHead);
    return ret; //返回头结点的下一个结点
}

OK,今天的讲解到这里就结束啦,我们下期再见!

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

光威神策PRO PCIe 5.0 SSD发布,国产固态硬盘进入10G俱乐部

全球半导体供应链的紧张局势和闪存资源的短缺让许多行业都面临着不小的压力 &#xff0c; 连带的也让消费者难以获取物美价廉的闪存产品 。但是&#xff0c;总有一些企业能够逆流而上&#xff0c; 像是 光威科技这家国产存储品牌&#xff0c; 最近就给国内消费者 带来了一个惊喜…

mybatis05:复杂查询:(多对一,一对多)

mybatis05&#xff1a;复杂查询&#xff1a;&#xff08;多对一&#xff0c;一对多&#xff09; 文章目录 mybatis05&#xff1a;复杂查询&#xff1a;&#xff08;多对一&#xff0c;一对多&#xff09;前言&#xff1a;多对一 &#xff1a; 关联 &#xff1a; 使用associatio…

Echarts-实现地图并轮播地图信息

目录 ./map-geojson/jinhua.json./CenterMap.vue./center.vue 使用地图组件效果 ./map-geojson/jinhua.json {"type":"FeatureCollection","features":[{"type":"Feature","properties":{"adcode":330…

力扣—2024 春招冲刺百题计划

矩阵 1. 螺旋矩阵 代码实现&#xff1a; /** param matrix int整型二维数组 * param matrixRowLen int matrix数组行数* param matrixColLen int* matrix数组列数* return int整型一维数组* return int* returnSize 返回数组行数 */ int* spiralOrder(int **matrix, int matri…

网工内推 | 网络工程师,13薪,周末双休,华三、华为认证优先

01 路邦远大 招聘岗位&#xff1a;网络工程师 职责描述&#xff1a; 1、配合市场销售人员&#xff0c;做好产品的售后服务工作&#xff1b; 2、负责项目方案安装调试指导以及日常客户使用培训&#xff0c;对客户提出的问题提出解决方案&#xff1b; 3、为客户提供专业、规范的…

图片作为背景的闪白问题,6种基础方案, 不会不知道吧

前言 关于【SSD系列】&#xff1a; 前端一些有意思的内容&#xff0c;旨在3-10分钟里&#xff0c; 500-1500字&#xff0c;有所获&#xff0c;又不为所累。 某天&#xff0c;发现有背景图片的弹出框&#xff0c;会出现闪白现象&#xff0c;这&#xff0c;兄弟们&#xff0c;你…

导入芯片原厂SDK Mirror源码到gerrit

下载镜像代码 repo init --mirror --repo-url ssh://xx/repo.git -u ssh://xx/manifests.git -m manifest.xml repo sync 创建AOSP project 对All Project权限修改 创建repo 在刚才下载的codebase根目录执行如下命令&#xff1a; repo forall -c echo $REPO_PROJECT; ssh -p 29…

C++ AVL树底层实现原理

&#x1f493;博主CSDN主页:麻辣韭菜&#x1f493;   ⏩专栏分类&#xff1a;C知识分享⏪   &#x1f69a;代码仓库:C高阶&#x1f69a;   &#x1f339;关注我&#x1faf5;带你学习更多C知识   &#x1f51d;&#x1f51d; 目录 前言 AVL 树 1.1 AVL树的概念 1.2 AVL树…

【Hadoop大数据技术】——Flume日志采集系统(学习笔记)

&#x1f4d6; 前言&#xff1a;在大数据系统的开发中&#xff0c;数据收集工作无疑是开发者首要解决的一个难题&#xff0c;但由于生产数据的源头丰富多样&#xff0c;其中包含网站日志数据、后台监控数据、用户浏览网页数据等&#xff0c;数据工程师要想将它们分门别类的采集…

永恒之蓝(ms17-010)复现

永恒之蓝 永恒之蓝&#xff08;Eternal Blue&#xff09;爆发于2017年4月14日晚&#xff0c;是一种利用Windows系统的SMB协议漏洞来获取系统的最高权限&#xff0c;以此来控制被入侵的计算机。甚至于2017年5月12日&#xff0c; 不法分子通过改造“永恒之蓝”制作了wannacry勒索…

ARM架构麒麟操作系统安装配置Mariadb数据库

、安装配置JDK (1)检查机器是否已安装JDK 执行 java -version命令查看机器是否安装JDK,一般麒麟操作系统默认安装openjdk 1.8。 (2)安装指定版本JDK 如果麒麟操作系统默认安装的openjdk 1.8不符合需求的话,可以卸载机器安装的openjdk 1.8并按需安装所需的openjdk版本…

软件杯 深度学习人体语义分割在弹幕防遮挡上的实现 - python

文章目录 1 前言1 课题背景2 技术原理和方法2.1基本原理2.2 技术选型和方法 3 实例分割4 实现效果5 最后 1 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是 &#x1f6a9; 深度学习人体语义分割在弹幕防遮挡上的应用 该项目较为新颖&#xff0c;适合作为竞…

Python爬虫与API交互:如何爬取并解析JSON数据

目录 前言 一、什么是API和JSON数据 二、准备环境 三、发送API请求并获取数据 四、解析JSON数据 五、完整代码示例 六、总结 前言 随着互联网的发展&#xff0c;越来越多的网站提供了API接口&#xff0c;供开发者获取实时数据。在爬虫领域中&#xff0c;与API交互并解析…

快速实现一个Hibernate的例子

写第一个简单的Hibernate程序&#xff1a; 具体的开始第一个Hibernate程序之前: 找到jar包, hibernate 的核心包, mysql数据库的连接驱动包, junit测试包 ①创建Hibernate配置文件 ②创建持久化类 也是和数据库中数据表一一对应这个类 ③创建对象-关系映射文件 ④通过hibern…

【攻防世界】mfw(.git文件泄露)

首先进入题目环境&#xff0c;检查页面、页面源代码、以及URL&#xff1a; 发现页面无异常。 使用 dirsearch 扫描网站&#xff0c;检查是否存在可访问的文件或者文件泄露&#xff1a; 发现 可访问界面/templates/ 以及 .git文件泄露&#xff0c;故使用 GItHack 来查看泄露的 …

LeetCode题练习与总结:不同路径Ⅱ--63

一、题目描述 一个机器人位于一个 m x n 网格的左上角 &#xff08;起始点在下图中标记为 “Start” &#xff09;。 机器人每次只能向下或者向右移动一步。机器人试图达到网格的右下角&#xff08;在下图中标记为 “Finish”&#xff09;。 现在考虑网格中有障碍物。那么从…

如何使用SQL注入工具?

前言 今天来讲讲SQL注入工具&#xff0c;sqlmap。如何使用它来一步步爆库。 sqlmap官方地址如下。 sqlmap: automatic SQL injection and database takeover tool 前期准备&#xff0c;需要先安装好docker、docker-compose。 一个运行的后端服务&#xff0c;用于写一个存在…

竞赛 图像识别-人脸识别与疲劳检测 - python opencv

文章目录 0 前言1 课题背景2 Dlib人脸识别2.1 简介2.2 Dlib优点2.3 相关代码2.4 人脸数据库2.5 人脸录入加识别效果 3 疲劳检测算法3.1 眼睛检测算法3.3 点头检测算法 4 PyQt54.1 简介4.2相关界面代码 5 最后 0 前言 &#x1f525; 优质竞赛项目系列&#xff0c;今天要分享的是…

AI大模型创新交汇点:当AI遇见艺术

博主猫头虎的技术世界 &#x1f31f; 欢迎来到猫头虎的博客 — 探索技术的无限可能&#xff01; 专栏链接&#xff1a; &#x1f517; 精选专栏&#xff1a; 《面试题大全》 — 面试准备的宝典&#xff01;《IDEA开发秘籍》 — 提升你的IDEA技能&#xff01;《100天精通鸿蒙》 …

【面试题】细说mysql中的各种锁

前言 作为一名IT从业人员&#xff0c;无论你是开发&#xff0c;测试还是运维&#xff0c;在面试的过程中&#xff0c;我们经常会被数据库&#xff0c;数据库中最经常被问到就是MySql。当面试官问MySql的时候经常会问道一个问题&#xff0c;”MySQL中有哪些锁&#xff1f;“当我…