当前位置: 首页 > article >正文

【C】链表算法题4 -- 合并两个有序链表

leetcode链接https://leetcode.cn/problems/merge-two-sorted-lists/description/https://leetcode.cn/problems/merge-two-sorted-lists/description/https://leetcode.cn/problems/merge-two-sorted-lists/description/

我们先来看一下题目描述
将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所有节点组成的。


示例1

输入:l1 = [1,2,4], l2 = [1,3,4]
输出:[1,1,2,3,4,4]


示例2

输入:l1 = [], l2 = []
输出:[]


函数头部

struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) 
{
    
}

很显然,这个题目是给你两个有序链表的首节点,让你返回合并之后链表的首节点。


 思路

  需要注意的是合并的两个链表是升序链表,也就是两个链表节点中的值是从小到大依次排列的。

  这个题的思路我们可以借鉴之前第一道链表算法题 -- 移除链表元素的思想,新建一个链表用来链接原来两个链表里面的较小的节点。同样,为了保存返回的节点的地址以及降低时间复杂度,所以这里仍选择创建两个指针变量 PHead 和 PTail 分别用来指向新链表的首节点和尾节点,再创建两个指针变量 l1 和 l2 分别指向 list1 和 list2 链表的首节点,然后让 l1 和 l2 分别遍历原来两个链表,每遍历一次就比较 l1 和 l2 指向节点 val 值的大小,然后让 val 值较小的尾插到新链表尾部,然后依次比较,直到其中一个链表遍历完成,循环就结束。

  但是题目要求把两个链表所有的节点都合并为一个新链表,但是只有一个链表遍历完,节点都尾插到新链表里面,不代表着两个链表的节点都尾插到了新链表里面,如示例1,当 l2 遍历完list2走到NULL时:

list1中还有一个节点并未插入到新链表中,所以还需要一个循环,用来将 list1 剩下的节点依次尾插到新链表中类似的,也有可能出现 list1 的节点全都尾插到新链表中, 而 list2 还有剩余的节点,所以也需要一个循环依次把 list2 的节点尾插到新链表中

  这段逻辑的代码为:

typedef struct ListNode ListNode;
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) 
{ 
  //创建新链表
  ListNode* PHead, *PTail;
  PHead = PTail = NULL;
  ListNode* l1 = list1, * l2 = list2;
  //将list1和list2中val值较小的节点尾差到新链表中
  while (l1 && l2)
  {

      if (l1->val < l2->val)
      {
          //判断新链表是否为空
          if (PHead == NULL)
          {
              PHead = PTail = l1;
              l1 = l1->next;
          }
          else
          {
              PTail->next = l1;
              l1 = l1->next;
              PTail = PTail->next;
          }
      }
      else
      {
          //判断链表是否为空
          if (PHead == NULL)
          {
              PHead = PTail = l2;
              l2 = l2->next;
          }
          else
          {
              PTail->next = l2;
              l2 = l2->next;
              PTail = PTail->next;
          }
      }
  }
  //将list1中还未插入的节点依次尾插到新链表中  
  while (l1)
  {
      PTail->next = l1;
      l1 = l1->next;
      PTail = PTail->next;
  }
  //将list2中还未插入的节点依次尾插到新链表中
  while (l2)
  {
      PTail->next = l2;
      l2 = l2->next;
      PTail = PTail->next;
  }
  
  return PHead;
}

  这里还需要考虑特殊情况,就是 list1 或者 list2 为空的情况,在这种情况下第一个循环是进不去的,而如果 list1 为空,list2 不为空,那么就会进入第二个循环,会发生对 patil ,也就是NULL指针的解引用,会发生错误同样,如果 list1 不为空,而 list2 为空,也会发生同样的情况。所以这里也需要特殊处理一下,如果 list1 为空,那就返回list2,如果 list2 为空,那就返回list1。完整代码如下:

代码

typedef struct ListNode ListNode;
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) 
{ 
  //判断两个链表为不为空
  if (list1 == NULL)
  {
      return list2;
  }
  if (list2 == NULL)
  {
      return list1;
  } 
  //创建新链表
  ListNode* PHead, *PTail;
  PHead = PTail = NULL;
  ListNode* l1 = list1, * l2 = list2;
  //将list1和list2中val值较小的节点尾差到新链表中
  while (l1 && l2)
  {

      if (l1->val < l2->val)
      {
          //判断新链表是否为空
          if (PHead == NULL)
          {
              PHead = PTail = l1;
              l1 = l1->next;
          }
          else
          {
              PTail->next = l1;
              l1 = l1->next;
              PTail = PTail->next;
          }
      }
      else
      {
          //判断链表是否为空
          if (PHead == NULL)
          {
              PHead = PTail = l2;
              l2 = l2->next;
          }
          else
          {
              PTail->next = l2;
              l2 = l2->next;
              PTail = PTail->next;
          }
      }
  }
  //将list1中还未插入的节点依次尾插到新链表中  
  while (l1)
  {
      PTail->next = l1;
      l1 = l1->next;
      PTail = PTail->next;
  }
  //将list2中还未插入的节点依次尾插到新链表中
  while (l2)
  {
      PTail->next = l2;
      l2 = l2->next;
      PTail = PTail->next;
  }
  
  return PHead;
}

 改进

  写完上面那个代码我们可以发现代码特别的长,原因就是其中有一段代码相同,就是在第一个循环里面,要判断新链表为不为空(PHead == NULL),所以这里我们可以改进一下,就是动态开辟一个节点,让PHead 与 PTail 都指向它,这样就不必判断链表为不为空了,也就省去了那段代码。

  如果这样改进的话,最后返回的是 PHead 指向节点的下一个节点的地址,需要在最后释放一下动态开辟的那个节点,当然不要忘记把PHead置为NULL,改进后的完整代码如下:

typedef struct ListNode ListNode;
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) 
{
    //list1为空的时候
    if (list1 == NULL)
    {
        return list2;
    }
    //list2为空的时候
    if (list2 == NULL)
    {
        return list1;
    }
    ListNode* l1 = list1, *l2 = list2;
    //新建一个带头节点的新链表
    ListNode* PHead,*PTail;
    PHead = PTail = (ListNode*)malloc(sizeof(ListNode));
    //开始尾插,谁小谁尾插
    while (l1 && l2)
    {
        if (l1->val < l2->val)
        {
            PTail->next = l1;
            l1 = l1->next;
            PTail = PTail->next;
        }
        else
        {
            PTail->next = l2;
            l2 = l2->next;
            PTail = PTail->next;
        }
    }
    //如果list1还有结点,尾插到链表中
    while (l1)
    {
        PTail->next = l1;
        l1 = l1->next;
        PTail = PTail->next;
    }
    如果list2还有结点,尾插到链表中
    while (l2)
    {
        PTail->next = l2;
        l2 = l2->next;
        PTail = PTail->next;
    }
    //实际返回的为新链表头节点的下一个结点
    ListNode* retnode = PHead->next;
    free(PHead);
    PHead = NULL;
    return retnode;
}

 


http://www.kler.cn/a/516485.html

相关文章:

  • 安卓14自由窗口圆角处理之绘制圆角轮廓线
  • 小游戏源码开发搭建技术栈和服务器配置流程
  • mysql 学习2 MYSQL数据模型,mysql内部可以创建多个数据库,一个数据库中有多个表;表是真正放数据的地方,关系型数据库 。
  • 由于请求的竞态问题,前端仔喜提了一个bug
  • 一个基于Python+Appium的手机自动化项目~~
  • Apache Hive3定位表并更改其位置
  • Spring MVC:深入理解与春招面试要点
  • Jenkins邮件通知的详细配置含邮件通知模板!
  • MyBatis-Plus的插件
  • 如何查找pom文件未使用的依赖
  • 窥探QCC518x-308x系列与手机之间的蓝牙HCI记录与分析 - 耳机篇
  • RabbitMQ2-简单案例
  • JVM深入学习(一)
  • 尚硅谷大数据数仓项目superset db upgrade报错解决(2025.1.23解决)
  • 云原生时代,如何构建高效分布式监控系统
  • OSCP - Proving Grounds - Quackerjack
  • C语言小任务——寻找水仙花数
  • springboot基于微信小程序的商城系统
  • CPU中断机制
  • Ubuntu 24.04 LTS 通过 docker desktop 安装 seafile 搭建个人网盘
  • 分词器的词表大小以及如果分词器的词表比模型的词表大,那么模型的嵌入矩阵需要被调整以适应新的词表大小。
  • MySQL命令及用法(精华版)
  • 接口 V2 完善:基于责任链模式、Canal 监听 Binlog 实现数据库、缓存的库存最终一致性
  • 2024 行远自迩,笃行不怠
  • Geek Uninstaller,绿色免安装轻量的应用卸载工具!
  • 微软预测 AI 2025,AI Agents 重塑工作形式