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

数据结构:合并两个单链表(merging two linked lists)

目录

第一步:问题本质分析

第二步:指针的角色定义

第三步:核心操作逻辑(滑动指针)

第四步:循环直到一个链表为空

第五步:代码实现


给定两个有序的单链表(升序),我们要把它们合并成一个新的有序链表,并且不新建节点,只复用原有节点。

数据结构:数组:合并数组(Merging Arrays)-CSDN博客

举个例子(升序合并):

List A: 1 → 3 → 5 → NULL  
List B: 2 → 4 → 6 → NULL

合并后:

Merged: 1 → 2 → 3 → 4 → 5 → 6 → NULL

第一步:问题本质分析

我们面对的是两个“有序序列”,本质是:

你要像“拉拉链”一样,从两个链表中选择当前最小的节点,接到结果链表中,然后往前推进那个节点

这其实就像是两个队列合并排序(归并排序 merge 步骤),但数据结构是链表。

❓问题1:我们能做的“最小动作”是什么?

答:我们能做的最小动作是:

比较 a1->datab1->data,谁小,谁应该排在合并链表最前面。

这是第一性操作单位,我们只能通过一次比较和一次 ->next 访问,来获取有限信息。

❓问题2:为什么我们需要一个“结果链表”?

你想最终有一个“新链表”,连接所有节点。但这些节点已经存在了,我们不用重新创建节点(否则浪费空间)。

所以我们只需要做一件事:

“连接指针”,让所有节点按顺序接起来。


第二步:指针的角色定义

❓问题3:我们能否只用一个变量维护合并链表?

设想我们一开始选定了合并链表的“第一个节点”。
比如第一次比较 a1b1,发现 a1->data < b1->data,我们就让:

merged_head = a1;

但这还不够,我们还需要维护一个指针:

→ 当前合并链表的最后一个节点(即尾指针)

否则我们就无法追加下一个节点。

推导到此,我们需要两个“操作级”指针:

  1. merged_head:记录整个合并链表的开头

  2. merged_tail:指向合并链表的最后一个节点,方便追加

几个基本指针变量来进行这项操作:

名称用途
first指向第一个链表当前节点
second指向第二个链表当前节点
merged_tail当前合并链表最后一个节点(尾指针)
merged_head指向合并后链表的头节点(第一次选中谁就赋值)

第三步:核心操作逻辑(滑动指针)

每一步我们都比较 first->datasecond->data

  • 取小的那个节点

  • 把它接到 merged_tail->next

  • 然后移动那个指针前进一步,否则下一轮还是同一个元素,这叫“消费当前节点”。

if (A->data < B->data) {merged_tail->next = A;A = A->next;
} else {merged_tail->next = B;B = B->next;
}
merged_tail = merged_tail->next;

  图解过程

A → [1] → [3] → [5]
B → [2] → [4] → [6]

执行顺序如下:

步骤比较选择Merged 链表
11 vs 21[1]
23 vs 22[1] → [2]
33 vs 43[1] → [2] → [3]
45 vs 44[1] → [2] → [3] → [4]
55 vs 65...
6NULL vs 66...

第四步:循环直到一个链表为空

❓问题4:谁是第一个节点?我们要不要特殊处理第一次?

第一次没有任何节点被加入,所以我们不能这样:

merged_tail->next = ...

因为 merged_tail 还没有定义,所以第一次我们要单独处理:

  • 第一次比较 A 和 B,谁小,谁就成为 merged_head

  • 也让 merged_tail 指向它,开始构建过程

这个操作写成:

if (A->data < B->data) {merged_head = A;merged_tail = A;A = A->next;
} else {merged_head = B;merged_tail = B;B = B->next;
}

为什么后面不需要再判断 merged_head?

因为我们一旦初始化了 merged_headmerged_tail,剩下的逻辑就完全一样了。

当任意一个链表先结束(指针变成 NULL),另一个链表的剩余部分就是升序链表,可以直接接在合并链表尾部

if (A == NULL)merged_tail->next = B;
elsemerged_tail->next = A;
操作本质解释
合并操作从两个有序链表中每次选最小的那个
指针处理用 tail 指针维护“已合并链表的结尾”
尾部接上最后只剩一个非空链表,直接接上
内存节省所有节点都是原链表复用,无需新建节点

第五步:代码实现

struct Node* merge(struct Node* A, struct Node* B) {struct Node* merged_head = NULL;struct Node* merged_tail = NULL;// Step 1: 处理第一个节点,决定 merged_headif (A == NULL) return B;if (B == NULL) return A;if (A->data < B->data) {merged_head = A;merged_tail = A;A = A->next;} else {merged_head = B;merged_tail = B;B = B->next;}// Step 2: 一边比较一边构建合并链表while (A != NULL && B != NULL) {if (A->data < B->data) {merged_tail->next = A;merged_tail = A;A = A->next;} else {merged_tail->next = B;merged_tail = B;B = B->next;}}// Step 3: 谁没处理完就接上谁if (A != NULL)merged_tail->next = A;elsemerged_tail->next = B;return merged_head;
}

时间与空间复杂度分析

方面分析
时间复杂度O(n + m),n 和 m 分别是两个链表的长度
空间复杂度O(1),因为不新建节点,只操作指针
http://www.dtcms.com/a/313777.html

相关文章:

  • 下面是修正后的完整版 doit_effects.c,已经做了三大关键修复(文件开头也有注释说明)
  • 使用opencv基于realsense D435i展示基本的图像
  • 如何基于MQ实现分布式事务
  • 深入浅出 RabbitMQ:简单队列实战指南
  • 消防器材检测数据集介绍-9,600 张图片 智慧安防系统 建筑施工安全监管 AI 消防巡检机器人 自动审核系统 公共场所安全监测
  • 深入解析线程同步中WaitForSingleObject的超时问题
  • Flutter 事件总线 Event Bus
  • 【2025WACV-最佳论文】RayGauss:基于体积高斯的光线投射,用于逼真的小说视图合成
  • 【机器学习】(算法优化二)提升算法之:AdaBoost与随机梯度
  • Java 中 BigDecimal、Float、Double 的取整与保留小数处理方法详解
  • 从 0 到 1 开发图书管理系统:飞算 JavaAI 让技术落地更简单
  • 13.Home-面板组件封装
  • 如何设计和实施高效的向量化数据检索解决方案
  • 阿里云-通义灵码:解锁云原生智能开发新能力,让云开发更“灵”~
  • Clion STM32CubeMX LED闪灯
  • 为什么叫电磁兼容?
  • 【Java】一篇详解HashMap的扩容机制!!
  • SCI论文选词炼句(下)
  • vue3指定设置了dom元素的ref但是为null问题
  • Druid手写核心实现案例 实现一个简单Select 解析,包含Lexer、Parser、AstNode
  • 第三章 浏览器 【5. 事件】
  • Java项目:基于SSM框架实现的电子病历管理系统【ssm+B/S架构+源码+数据库+毕业论文+远程部署】
  • 前端开发(HTML,CSS,VUE,JS)从入门到精通!第五天(jQuery函数库)
  • 深入理解Java的SPI机制,使用auto-service库优化SPI
  • 打造个人数字图书馆:LeaNote+cpolar如何成为你的私有化知识中枢?
  • 【MySQL02】: MySQL类型
  • 深度学习TR3周:Pytorch复现Transformer
  • 软件测试自学之路
  • 架构师面试(三十九):微服务重构单体应用
  • 第三阶段—8天Python从入门到精通【itheima】-143节(pyspark实战——数据计算——flatmap方法)