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

文章目录

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

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

问题描述

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

示例:

复制代码
输入: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),不需要额外空间,直接在原节点上操作

完整代码实现

c 复制代码
/**
 * 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
}

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

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

3. 节点比较与插入

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

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

4. 剩余节点处理

复制代码
if (l1 == NULL) {
    NewTail->next = l2;
} else {
    NewTail->next = l1;
}

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

常见错误与修正

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

c 复制代码
// 错误写法(赋值而非比较)
if(NewHead=NULL) 

// 正确写法(比较操作)
if(NewHead == NULL)

这个错误会导致:

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

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

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

优化方案:哨兵节点

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

c 复制代码
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个有序链表)

  1. 数据库系统 :合并多个有序结果集

  2. 消息队列:合并多个有序消息流

总结

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

  • 核心思想:双指针遍历+尾插法
  • 关键技巧:头尾指针维护新链表
  • 常见陷阱:头节点初始化、指针操作顺序
  • 优化方向:哨兵节点简化边界处理
相关推荐
白白白小纯9 小时前
C++进阶.多态
c++
Navigator_Z9 小时前
LeetCode //C - 1300. Sum of Mutated Array Closest to Target
c语言·算法·leetcode
SZYSK9 小时前
鸿蒙人脸识别算法放在终端里:和外挂授权 SDK 差在排障路径
物联网·算法·华为·harmonyos
若亦_9 小时前
数据结构>>优先级队列(堆)
java·数据结构
程序猿乐锅10 小时前
【从0-1学习Python | 第七篇】Python 面向对象基础
开发语言·笔记·后端·python·学习·1024程序员节
fish_zzzz10 小时前
笔记--浅谈内存,存储空间,BIOS芯片,显卡GPU、VRAM(1)
笔记·1024程序员节
凯瑟琳.奥古斯特10 小时前
力扣1016高效解法:剪枝优化技巧
开发语言·c++·算法·leetcode·职场和发展
ACP广源盛1392462567310 小时前
PCIe Gen3 交换芯片选型笔记:多路扩展方案 IX7024@ACP 评估
笔记·硬件架构·pcie·国产芯片
深圳老胡10 小时前
STM32 开发中 Trae 内置模型怎么选:Seed、MiniMax、GLM、DeepSeek、Kimi、Qwen 全对比
笔记·stm32·单片机·嵌入式硬件·代码规范·1024程序员节
bksczm10 小时前
Redis数据结构之hash
数据结构·redis·哈希算法·1024程序员节