【数据结构与算法】21.合并两个有序链表(LeetCode)

文章目录

  • 合并两个有序链表:高效算法解析与实现
    • 问题描述
    • 核心思路:双指针尾插法
    • 完整代码实现
    • 关键点解析
      • 1. 边界条件处理
      • 2. 头节点初始化
      • 3. 节点比较与插入
      • 4. 剩余节点处理
    • 常见错误与修正
    • 优化方案:哨兵节点
    • 算法应用场景
    • 总结
    • 总结

合并两个有序链表:高效算法解析与实现

链表合并是数据结构中的经典问题,在算法面试和实际开发中经常出现。本文将深入解析如何高效合并两个有序链表,并展示C语言的实现方案。

问题描述

在这里插入图片描述

给定两个升序排列的链表list1list2,要求将它们合并为一个新的升序链表并返回。新链表应该通过拼接给定链表的节点来完成。

示例:

输入:list1 = [1,2,4], list2 = [1,3,4]
输出:[1,1,2,3,4,4]

核心思路:双指针尾插法

基本思想:

  1. 创建一个新的空链表作为结果
  2. 使用两个指针分别遍历两个输入链表
  3. 比较当前节点的值,将较小值的节点插入新链表的尾部
  4. 当任一链表遍历完后,将剩余链表直接接到新链表尾部

时间复杂度: O(n+m),其中n和m分别是两个链表的长度
空间复杂度: O(1),不需要额外空间,直接在原节点上操作

完整代码实现

/*** Definition for singly-linked list.* struct ListNode {*     int val;*     struct ListNode *next;* };*/
struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) {struct ListNode* l1 = list1;struct ListNode* l2 = list2;struct ListNode* NewHead = NULL;struct ListNode* NewTail = NULL;// 处理空链表的情况if (l1 == NULL) return l2;if (l2 == NULL) return l1;// 遍历两个链表while (l1 && l2) {if (l1->val < l2->val) {// 处理新链表的头节点if (NewHead == NULL) {NewHead = NewTail = l1;} else {NewTail->next = l1;NewTail = NewTail->next;}l1 = l1->next;} else {// 处理新链表的头节点if (NewHead == NULL) {NewHead = NewTail = l2;} else {NewTail->next = l2;NewTail = NewTail->next;}l2 = l2->next;}}// 连接剩余链表if (l1 == NULL) {NewTail->next = l2;} else {NewTail->next = l1;}return NewHead;
}

关键点解析

1. 边界条件处理

if (l1 == NULL) return l2;
if (l2 == NULL) return l1;

这两行代码处理了空链表的边界情况,提高了代码的健壮性。

2. 头节点初始化

if (NewHead == NULL) {NewHead = NewTail = l1; // 或l2
}

这里使用NewHeadNewTail两个指针分别记录新链表的头和尾:

  • NewHead:始终指向新链表的头节点
  • NewTail:始终指向新链表的尾节点,便于尾插操作

3. 节点比较与插入

if (l1->val < l2->val) {// 插入l1节点
} else {// 插入l2节点
}

通过比较两个链表当前节点的值,决定哪个节点应该优先插入新链表,确保结果保持升序。

4. 剩余节点处理

if (l1 == NULL) {NewTail->next = l2;
} else {NewTail->next = l1;
}

当任一链表遍历完后,直接将另一链表的剩余部分连接到新链表尾部,避免了不必要的循环。

常见错误与修正

在原始代码中,存在一个典型错误:

// 错误写法(赋值而非比较)
if(NewHead=NULL) // 正确写法(比较操作)
if(NewHead == NULL)

这个错误会导致:

  1. NewHead设置为NULL
  2. 条件判断结果永远为假(NULL相当于0)
  3. 永远不会进入头节点初始化分支

开发建议: 在条件判断中使用常量在前的方式避免此类错误:

if (NULL == NewHead) // 如果误写为赋值,编译器会报错

优化方案:哨兵节点

使用哨兵节点可以进一步简化代码逻辑:

struct ListNode* mergeTwoLists(struct ListNode* list1, struct ListNode* list2) {struct ListNode dummy;  // 哨兵节点struct ListNode* tail = &dummy;dummy.next = NULL;while (list1 && list2) {if (list1->val < list2->val) {tail->next = list1;list1 = list1->next;} else {tail->next = list2;list2 = list2->next;}tail = tail->next;}// 连接剩余部分tail->next = list1 ? list1 : list2;return dummy.next;  // 哨兵的下一个节点即真实头节点
}

哨兵节点方案的优点:

  1. 消除头节点特殊判断
  2. 减少代码分支(从4个分支减少到2个)
  3. 提高代码可读性和健壮性
  4. 避免头节点指针的初始化问题

算法应用场景

  1. 归并排序:链表归并排序的核心操作
  2. 多路归并:多个有序流的合并(如K个有序链表)
  3. 数据库系统:合并多个有序结果集
  4. 消息队列:合并多个有序消息流

总结

合并两个有序链表是链表操作中的基础但重要的算法:

  • 核心思想:双指针遍历+尾插法
  • 关键技巧:头尾指针维护新链表
  • 常见陷阱:头节点初始化、指针操作顺序
  • 优化方向:哨兵节点简化边界处理

多路归并*:多个有序流的合并(如K个有序链表)
3. 数据库系统:合并多个有序结果集
4. 消息队列:合并多个有序消息流

总结

合并两个有序链表是链表操作中的基础但重要的算法:

  • 核心思想:双指针遍历+尾插法
  • 关键技巧:头尾指针维护新链表
  • 常见陷阱:头节点初始化、指针操作顺序
  • 优化方向:哨兵节点简化边界处理

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

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

相关文章

gd32modbus从机移植

文章目录1. 背景2. 改写方式2.1 cursor2.2 使用方式3. 移植过程修改概述修改的文件和内容1. PRO2/Core/Inc/usart.h2. PRO2/Core/Src/usart.c3. PRO2/Drivers/BSP/STM32MB/port/portserial.c4. PRO2/Core/Src/stm32f1xx_it.c5. PRO2/Core/Src/main.c6. PRO2/Core/Src/gpio.c引脚…

PCB 控深槽如何破解 5G 基站 120℃高热魔咒?

5G 基站在高频通信下的功耗较 4G 基站提升 3-4 倍&#xff0c;射频模块、电源单元等核心部件的工作温度常突破 120℃&#xff0c;远超设备安全阈值&#xff08;≤85℃&#xff09;&#xff0c;形成制约通信稳定性的 “高热魔咒”。印制线路板&#xff08;PCB&#xff09;作为热…

Linux和shell

最快入门的方式是使用苹果系统。此外&#xff0c;累计补充学习&#xff1a;一、目录结构/bin&#xff0c;二进制文件 /boot&#xff0c;启动文件 /dev&#xff0c;设备文件 /home&#xff0c;主目录&#xff0c;一般外接包、安装包放在这里 /lib&#xff0c;库文件 /opt&#x…

Vue多请求并行处理实战指南

在 Vue 中同时发送多个请求主要通过并行处理机制实现&#xff0c;常用方法包括 Promise.all、axios.all&#xff08;基于 Axios 库&#xff09;和 async/await。以下为详细操作指南和注意事项&#xff1a; 一、使用 Promise.all 并行发送请求 Promise.all 接收一个 Promise 数组…

Redis线程模型讨论

很多人常说&#xff0c;因为 Redis 是单线程的&#xff0c;所以它的操作就快、性能就好。但其实这个表述并不完全准确&#xff0c;因为 Redis 作为一个成熟的分布式缓存框架&#xff0c;它由很多模块组成&#xff0c;如网络请求模块、数据操作模块、存储模块、索引模块、高可用…

基于Spring Boot实现中医医学处方管理实践

基于Spring Boot实现中医医学处方管理 以下是基于Spring Boot实现中医医学处方管理的相关示例和资源整理,涵盖基础架构、功能模块及实际应用案例: 基础项目结构 Spring Boot中医处方系统通常采用MVC分层设计: 实体类:定义处方、药材、患者等JPA实体 @Entity public clas…

中宇联:以“智云融合+AI”赋能全栈云MSP服务,深化阿里云生态合作

作为国内领先的全栈云MSP服务商&#xff0c;中宇联依托自主研发的“智云融合AI”服务平台&#xff0c;为企业提供涵盖云架构设计、迁移实施到云优化服务等内容的端到端解决方案&#xff0c;助力客户高效利用云端资源、实现业务创新。一、中宇联云MSP服务能力全景中宇联以混合云…

分布式微服务--万字详解 微服务的各种负载均衡全场景以注意点

前言&#xff1a;1. 使用方式分类总览序号使用形式是否基于服务名调用是否需 LoadBalanced备注1RestTemplate 自定义负载均衡❌ 否&#xff08;手动拼接URL&#xff09;❌ 否手动选择服务实例2RestTemplate Ribbon&#xff08;非服务名&#xff09;❌ 否&#xff08;手动拼接…

Netty的Http解码器源码分析

一、HTTP协议简介HTTP&#xff08;HyperText Transfer Protocol&#xff0c;超文本传输协议&#xff09;是一种基于 请求-响应模型 的无状态应用层协议&#xff0c;广泛用于客户端&#xff08;如浏览器&#xff09;和服务器之间的数据通信。其主要特点包括&#xff1a;基于 TCP…

磁盘io查看命令iostat与网络连接查看命令netstat

1. iostat的使用场景首先iostat命令隶属于sysstat软件包。iostat专门用来查看主机上每个磁盘设备的io情况&#xff0c;包括像每秒的读写数据情况&#xff0c;磁盘平均io时间&#xff0c;设备io繁忙情况等等。1.1 iostat的普通输出解释首先是主机的架构&#xff0c;主机名&#…

Linux ps -ef 命令解析

ps 是 Linux 系统中用于查看进程状态的标准命令&#xff0c;-ef 是其参数组合&#xff0c;用于输出系统范围内所有进程的完整信息。以下是对该参数的详细解析&#xff1a; 1. 核心参数含义-e表示显示所有进程&#xff08;包括系统进程和用户进程&#xff09;&#xff0c;相当于…

2025年湖北中级注册安全工程师报考那些事

2025年湖北中级注册安全工程师报考那些事各位从事建筑安全的人员看过来&#xff0c;注册安全工程师是你们行业认可度较为高的证书。关于报考无论是安全相关专业跟不相关的专业都是可以报考的。只是年份要求不同。 本科&#xff1a;相关专业3年&#xff0c;不相关专业4年。 专科…