利用编程思维做题之将两个有序的单链表合并成一个新的有序单链表

news/2024/10/20 12:07:41/

1. 理解问题

        将两个有序的单链表合并成一个新的单链表,并且保持有序。每个链表的元素按照升序排列,合并后的链表也需要保持升序。

示例:

假设我们有两个有序链表:

  • 链表 1:1 -> 3 -> 5
  • 链表 2:2 -> 4 -> 6

合并后的链表应该是:1 -> 2 -> 3 -> 4 -> 5 -> 6

关键点:

  • 两个链表中的每个结点都已经有序
  • 我们需要逐步比较两个链表的当前结点,并将较小的结点插入新的链表中,直到两个链表都为空。

2. 输入输出

输入:

  • 两个有序单链表的头结点 head1head2

输出:

  • 合并后新的有序单链表的头结点。

3. 链表结构

每个链表结点的定义如下:

struct ListNode {
    int val;                // 节点的值
    struct ListNode *next;  // 指向下一个节点的指针
};
 

4. 制定策略

        为了将两个有序单链表合并为一个有序单链表,我们可以采用以下步骤:

  1. 初始化一个新的链表头:创建一个哑节点 dummy,它将帮助我们简化链表的操作,并最终返回它的下一个结点作为合并后的链表头。

  2. 逐步比较两个链表的结点:

    • 比较两个链表当前结点的值,将较小的值添加到链表中。
    • 移动指针,使得每次比较之后,较小值的链表向后移动一个结点。
  3. 处理剩余的结点:

    • 如果其中一个链表先为空,则将另一个链表剩余的所有结点直接链接到新链表的末尾。
  4. 返回合并后的链表:返回哑节点的下一个结点作为合并后链表的头结点。

5. 实现代码

5.1 关键函数实现

#include <stdio.h>
#include <stdlib.h>

// 定义链表节点
struct ListNode {
    int val;
    struct ListNode* next;
};

// 创建新节点
struct ListNode* createNode(int val) {
    struct ListNode* newNode = (struct ListNode*)malloc(sizeof(struct ListNode));
    newNode->val = val;
    newNode->next = NULL;
    return newNode;
}

// 合并两个有序链表
struct ListNode* mergeTwoLists(struct ListNode* head1, struct ListNode* head2) {
    // 创建一个哑节点,便于处理头节点
    struct ListNode* dummy = createNode(-1);
    struct ListNode* curr = dummy;  // 新链表的当前指针

    // 逐步比较两个链表的结点
    while (head1 != NULL && head2 != NULL) {
        if (head1->val <= head2->val) {
            curr->next = head1;
            head1 = head1->next;
        } else {
            curr->next = head2;
            head2 = head2->next;
        }
        curr = curr->next;  // 移动到新链表的下一个位置
    }

    // 将剩余的结点直接链接到新链表末尾
    if (head1 != NULL) {
        curr->next = head1;
    } else {
        curr->next = head2;
    }

    // 返回合并后的链表(跳过哑节点)
    struct ListNode* mergedHead = dummy->next;
    free(dummy);
    return mergedHead;
}

// 打印链表
void printList(struct ListNode* head) {
    while (head != NULL) {
        printf("%d -> ", head->val);
        head = head->next;
    }
    printf("NULL\n");
}

5.2 模拟过程

        假设我们有两个链表:

  • 链表1:1 -> 3 -> 5
  • 链表2:2 -> 4 -> 6

Step 1:初始化哑节点 dummycurr指向dummy

Step 2:比较链表1和链表2的第一个结点,1 <= 2,将1添加到新链表。

Step 3:比较链表1和链表2的下一个结点,3 > 2,将2添加到新链表。

重复这个过程,直到处理完两个链表中的所有结点。

最终合并的链表为:1 -> 2 -> 3 -> 4 -> 5 -> 6 -> NULL

5.3 完整 C 代码

#include <stdio.h>
#include <stdlib.h>

// 定义链表节点
struct ListNode {
    int val;
    struct ListNode* next;
};

// 创建新节点
struct ListNode* createNode(int val) {
    struct ListNode* newNode = (struct ListNode*)malloc(sizeof(struct ListNode));
    newNode->val = val;
    newNode->next = NULL;
    return newNode;
}

// 合并两个有序链表
struct ListNode* mergeTwoLists(struct ListNode* head1, struct ListNode* head2) {
    struct ListNode* dummy = createNode(-1);
    struct ListNode* curr = dummy;

    while (head1 != NULL && head2 != NULL) {
        if (head1->val <= head2->val) {
            curr->next = head1;
            head1 = head1->next;
        } else {
            curr->next = head2;
            head2 = head2->next;
        }
        curr = curr->next;
    }

    if (head1 != NULL) {
        curr->next = head1;
    } else {
        curr->next = head2;
    }

    struct ListNode* mergedHead = dummy->next;
    free(dummy);
    return mergedHead;
}

// 打印链表
void printList(struct ListNode* head) {
    while (head != NULL) {
        printf("%d -> ", head->val);
        head = head->next;
    }
    printf("NULL\n");
}

int main() {
    // 创建链表1:1 -> 3 -> 5
    struct ListNode* list1 = createNode(1);
    list1->next = createNode(3);
    list1->next->next = createNode(5);

    // 创建链表2:2 -> 4 -> 6
    struct ListNode* list2 = createNode(2);
    list2->next = createNode(4);
    list2->next->next = createNode(6);

    printf("链表1:\n");
    printList(list1);
    printf("链表2:\n");
    printList(list2);

    // 合并链表
    struct ListNode* mergedList = mergeTwoLists(list1, list2);
    printf("合并后的链表:\n");
    printList(mergedList);

    return 0;
}

5.4 代码说明
  • createNode:创建一个新的链表结点。
  • mergeTwoLists:将两个有序链表合并为一个新的有序链表,并返回合并后的链表头。
  • printList:打印链表内容。

6. 时间和空间复杂度分析

  • 时间复杂度:O(m + n),其中 m 和 n 分别是两个链表的长度。因为我们需要遍历两个链表的所有结点进行合并。
  • 空间复杂度:O(1),因为我们只使用了常数级别的额外空间(不包括输入链表的存储空间)。

7. 总结

        通过该算法,我们能够高效地将两个有序单链表合并为一个新的有序链表。该算法的核心是通过逐步比较两个链表的当前结点,维护一个合并后的链表。


http://www.ppmy.cn/news/1538693.html

相关文章

python爬虫 - 深入requests模块

&#x1f308;个人主页&#xff1a;https://blog.csdn.net/2401_86688088?typeblog &#x1f525; 系列专栏&#xff1a;https://blog.csdn.net/2401_86688088/category_12797772.html 目录 前言 一、下载网络文件 &#xff08;一&#xff09;基本步骤 &#xff08;二&…

【C++几种单例模式解读及实现方式】

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、单例是什么&#xff1f;二、解读1.懒汉式2.饿汉式3.static变量特性4.call_once特性 总结 前言 单例模式几乎是每种语言都不可少的一种设计模式&#xff0c…

Redis 典型应用之缓存

目录 1. 缓存的基本概念 2. 使用 Redis 作为缓存 3. 缓存的更新策略 3.1 定期生成 3.2 实时生成 3.2.1 内存淘汰策略 1. FIFO (First In First Out) 先进先出 2. LRU (Least Recently Used) 淘汰最久未使使用的 3. LFU (Least Frequently Used) 淘汰访问次数最少的 4…

【uni-app】HBuilderX安装uni-ui组件

目录 1、官网找到入口 2、登录帐号 3、打开HuilderX 4、选择要应用的项目 5、查看是否安装完成 6、按需安装 7、安装完毕要重启 8、应用 前言&#xff1a;uniapp项目使用uni-ui组件方式很多&#xff0c;有npm安装等&#xff0c;或直接创建uni-ui项目&#xff0c;使用un…

操作系统15

设备分配与回收 1.数据结构&#xff1a;系统设备表、设备控制表、控制器控制表、通道控制表 2.分配原则 &#xff08;1&#xff09;要充分发挥设备的使用率&#xff0c;尽可能让设备忙碌&#xff0c;但又避免由于不合理的分配方法造成的死锁 &#xff08;2&#xff09;要做到…

报错 - llama-index pydantic error | arbitrary_types_allowed | PydanticUserError

国庆节前使用 LiteLLMEmbedding 设置 llama-index Settings.embed_model 还好好的&#xff0c;回来后&#xff0c;就就报错&#xff0c;试着降级 llama-index 也无用&#xff1b;设置 Settings.llm 也是好好地。 解决方法&#xff1a;conda 重新创建环境后&#xff0c;在安装 …

CVPR 2024最佳论文候选-pixelSplat论文解读

目录 一、概述 二、相关工作 1、单场景下的视角合成 2、基于先验的三维重建和视图合成 3、多视图几何测量 三、3DGS的缺点 1、容易陷入最小值 2、需要大量输入图像 3、尺度模糊性 四、pixelSplat 1、解决尺度模糊性&#xff08;深度信息生成&#xff09; 2、编码器…

计算机挑战赛4

对于给定的十进制整数N(N<100000)&#xff0c;将1到N(含N)之间的每个整数转成八进制&#xff0c;求转换后的所有八进制数中含7的总个数。 提示:某个数的八进制含7的个数可以参照下面的例子: 对于整数127&#xff0c;对应的八进制为177&#xff0c;其含7的个数为2。 输入说明…