【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 = []
输出:[]


函数头部

cpp 复制代码
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 的节点尾插到新链表中

这段逻辑的代码为:

cpp 复制代码
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。完整代码如下:

代码

cpp 复制代码
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,改进后的完整代码如下:

cpp 复制代码
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;
}

相关推荐
骑自行车的码农8 分钟前
【React用到的一些算法】游标和栈
算法·react.js
博笙困了32 分钟前
AcWing学习——双指针算法
c++·算法
moonlifesudo1 小时前
322:零钱兑换(三种方法)
算法
NAGNIP19 小时前
大模型框架性能优化策略:延迟、吞吐量与成本权衡
算法
美团技术团队20 小时前
LongCat-Flash:如何使用 SGLang 部署美团 Agentic 模型
人工智能·算法
Fanxt_Ja1 天前
【LeetCode】算法详解#15 ---环形链表II
数据结构·算法·leetcode·链表
侃侃_天下1 天前
最终的信号类
开发语言·c++·算法
茉莉玫瑰花茶1 天前
算法 --- 字符串
算法
博笙困了1 天前
AcWing学习——差分
c++·算法
NAGNIP1 天前
认识 Unsloth 框架:大模型高效微调的利器
算法