【C++算法】54.链表_合并 K 个升序链表

news2025/4/18 0:59:08

文章目录

    • 题目链接:
    • 题目描述:
    • 解法
    • C++ 算法代码:


题目链接:

23. 合并 K 个升序链表


题目描述:

4e3bdacde5401dc116a26c2bca8e42fa


解法

解法一:暴力解法

每个链表的平均长度为n,有k个链表,时间复杂度O(nk^2)

合并两个有序链表

先把其中的两个链表有序合并,然后把合并后的链表和后面一个链表有序合并,每次合并两个直到结束。

解法二:利用优先级队列做优化O(n*k*logk)

先给n个链表设置n个指针,然后把每个链表的第一个指针放入小根堆里面,取走堆顶元素放入newhead节点,哪一个链表的指针取走了就让那个链表指针的后一个位置放进小根堆,直到所有链表指针都指向NULL

633f4dcf38470d0f105f421514f8a803

解法三:归并排序,递归O(n*k*logk)

536ee24d1e22a89395105c6e06d2f93c


C++ 算法代码:

解法二(利用堆)

/**
 * Definition for singly-linked list.
 * struct ListNode {
 * int val;
 * ListNode *next;
 * ListNode() : val(0), next(nullptr) {}
 * ListNode(int x) : val(x), next(nullptr) {}
 * ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution 
{
    // 定义比较器结构体,用于小根堆的节点比较
    struct cmp
    {
        // 重载函数调用运算符,定义节点比较规则
        // 对于小根堆,我们需要大值返回true(表示优先级低)
        bool operator()(const ListNode* l1, const ListNode* l2)
        {
            return l1->val > l2->val; // 如果l1值大于l2,就向下调整,返回true,实现小根堆
        }
    };
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) 
    {
        // 创建一个小根堆,存储ListNode指针
        // 堆会根据节点的val值自动排序,最小的在堆顶
        // 第一个参数 ListNode*: 指定了队列中存储的元素类型,这里是链表节点的指针
		// 第二个参数 vector<ListNode*>: 指定了底层容器类型,这里使用vector存储ListNode指针
		// 第三个参数 cmp: 指定了元素比较的方式,是一个自定义的比较器结构体
        priority_queue<ListNode*, vector<ListNode*>, cmp> heap;

        // 将所有链表的头节点加入小根堆
        // 只有非空链表的头节点才会被加入
        // auto l 会依次获取数组中的每个元素,即每个链表的头指针
        for(auto l : lists)
            if(l) heap.push(l);

        // 创建虚拟头节点,简化链表构建过程
        ListNode* ret = new ListNode(0);
        ListNode* prev = ret; // 使用prev作为结果链表的尾指针

        // 不断从堆中取出最小节点,加入结果链表
        while(!heap.empty())
        {
            ListNode* t = heap.top(); // 取出堆顶元素(当前所有节点中值最小的)
            heap.pop(); // 移除堆顶元素

            prev->next = t; // 将最小节点添加到结果链表
            prev = t; // 更新尾指针

            // 如果当前取出的节点还有下一个节点,将其加入堆中
            if(t->next) heap.push(t->next);
        }

        // 获取结果链表的头节点(跳过虚拟头节点)
        prev = ret->next;
        delete ret; // 释放虚拟头节点
        return prev; // 返回合并后的链表头
    }
};

解法三(递归/分治)

/**
 * Definition for singly-linked list.
 * struct ListNode {
 * int val;
 * ListNode *next;
 * ListNode() : val(0), next(nullptr) {}
 * ListNode(int x) : val(x), next(nullptr) {}
 * ListNode(int x, ListNode *next) : val(x), next(next) {}
 * };
 */
class Solution 
{
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) 
    {
        // 主函数入口:调用递归函数处理整个链表数组
        return merge(lists, 0, lists.size() - 1);
    }

    // 分治递归函数:合并区间[left, right]内的链表
    ListNode* merge(vector<ListNode*>& lists, int left, int right)
    {
        // 边界情况:如果left大于right,说明没有链表可合并
        if(left > right) return nullptr;

        // 边界情况:如果只有一个链表,直接返回
        if(left == right) return lists[left];

        // 1. 计算中间位置,将链表数组分为两部分
        int mid = left + right >> 1; // 等价于 (left + right) / 2

        // 2. 递归处理左右两个区间
        // 左区间: [left, mid]
        ListNode* l1 = merge(lists, left, mid);
        // 右区间: [mid + 1, right]
        ListNode* l2 = merge(lists, mid + 1, right);

        // 3. 合并两个区间返回的链表
        return mergeTowList(l1, l2);
    }

    // 合并两个有序链表
    ListNode* mergeTowList(ListNode* l1, ListNode* l2)
    {
        // 处理边界情况
        if(l1 == nullptr) return l2;
        if(l2 == nullptr) return l1;

        // 创建一个临时头节点,简化合并逻辑
        ListNode head;
        ListNode* cur1 = l1;       // 指向第一个链表的当前节点
        ListNode* cur2 = l2;       // 指向第二个链表的当前节点
        ListNode* prev = &head;    // 指向合并结果的尾节点
        head.next = nullptr;       // 初始化临时头节点

        // 合并两个链表,每次取值较小的节点
        while(cur1 && cur2)
        {
            if(cur1->val <= cur2->val)
            {
                // 第一个链表的当前节点值更小,加入结果
                prev = prev->next = cur1;
                cur1 = cur1->next;
            }
            else
            {
                // 第二个链表的当前节点值更小,加入结果
                prev = prev->next = cur2;
                cur2 = cur2->next;
            }
        }

        // 处理剩余节点(只需处理一个链表的剩余部分)
        if(cur1) prev->next = cur1;
        if(cur2) prev->next = cur2;

        // 返回合并后的链表头节点
        return head.next;
    }
};

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

EG8200Mini-104边缘计算网关!聚焦IEC104协议的工业数据转换与远程运维平台

在工业自动化和信息化融合不断深化的背景下&#xff0c;现场设备的数据采集与协议转换能力对系统集成效率与运维成本产生着直接影响。EG8200Mini-104边缘计算网关正是基于此需求场景设计&#xff0c;具备IEC104主从站双向支持能力&#xff0c;并配套远程运维与多网络接入方案&a…

python多线程+异步编程让你的程序运行更快

多线程简介 多线程是Python中实现并发编程的重要方式之一&#xff0c;它允许程序在同一时间内执行多个任务。在某些环境中使用多线程可以加快我们代码的执行速度&#xff0c;例如我们通过爬虫获得了一个图片的url数组&#xff0c;但是如果我们一个一个存储很明显会非常缓慢&…

各种场景的ARP攻击描述笔记(超详细)

1、ARP报文限速 上一章我们说过ARP报文也是需要上送CPU进行处理的协议报文,如果设备对收到的大量ARP报文全部进行处理,可能导致CPU负荷过重而无法处理其他业务。因此,在处理之前需要对ARP报文进行限速,以保护CPU资源。 1.根据源MAC地址或源IP地址进行ARP限速 当设备检测到某一…

庙算兵推:使用Streamlit框架构建了一个智能作战推演系统。

这段代码是一个完整的军事模拟应用&#xff0c;使用Streamlit框架构建了一个智能作战推演系统。该系统包括了三维地图显示、作战单位管理、应急事件处理等功能。用户可以通过界面控制推演的开始和暂停&#xff0c;调整时间加速倍率&#xff0c;并查看实时的战斗情况和系统状态。…

HDCP(四)

HDCP驱动开发实战深度解析 以下从协议栈架构、核心模块实现、安全设计到硬件集成&#xff0c;结合HDCP 2.x规范与主流硬件平台&#xff08;如ARM、FPGA&#xff09;特性&#xff0c;系统拆解驱动开发关键环节&#xff1a; 1. 协议栈架构与模块划分 驱动分层设计 硬件抽象层&…

Docker MySQL的主从同步 数据备份 数据同步 配置文件

创建主库 docker run \--namemysql_1 \-e MYSQL_ROOT_PASSWORD123456 \-p 3306:3306 \-v mysql_main_data:/var/lib/mysql \--restart unless-stopped \-d \mysql:8.0进入容器内部 docker exec -it mysql_1 bash查找配置文件 find / -name my.cnf复制出主机 docker cp mysql…

996引擎-疑难杂症:Ctrl + F9 编辑好的UI进入游戏查看却是歪的

Ctrl F9 编辑好UI后&#xff0c;进入游戏查看却是歪的。 检查Ctrl F10 是否有做过编辑。可以找到对应界面执行【清空】

JQuery初步学习

文章目录 一、前言二、概述2.1 介绍2.2 安装 三、语法3.1 文档就绪3.2 选择器 四、事件4.1 概述4.2 事件绑定/解绑4.3 一次性事件4.4 事件委托4.5 自定义事件 五、效果5.1 隐藏/显示5.2 淡入淡出5.3 滑动5.4 动画 六、链七、HTML7.1 内容/属性7.2 元素操作7.3 类属性7.4 样式属…

基于 Spring Boot 瑞吉外卖系统开发(三)

基于 Spring Boot 瑞吉外卖系统开发&#xff08;三&#xff09; 分类列表 静态页面 实现功能所需要的接口 定义Mapper接口 Mapper public interface CategoryMapper extends BaseMapper<Category> {}定义Service接口 public interface CategoryService extends ISe…

winserver2022备份

安装备份&#xff0c;然后等待安装完成即可 然后可以在这里看到安装好的win server2022备份 一直下一步然后到这里 不要用本地文件夹备份 备份到远程服务器&#xff0c;远程服务器路径 然后确定备份即可 如何恢复呢&#xff1f; 点击右侧的恢复就可以了 打开任务计划程序 这…

GAT-GRAPH ATTENTION NETWORKS(论文笔记)

CCF等级&#xff1a;A 发布时间&#xff1a;2018年 代码位置 25年4月21日交 目录 一、简介 二、原理 1.注意力系数 2.归一化 3.特征组合与非线性变换 4.多头注意力 4.1特征拼接操作 4.2平均池化操作 三、实验性能 四、结论和未来工作 一、简介 图注意力网络&…

PDFBox/Itext5渲染生成pdf文档

目录 PDFBox最终效果实现代码 Itext5最终效果实现代码 PDFBox 使用PDFBox可以渲染生成pdf文档&#xff0c;并且自定义程度高&#xff0c;只是比较麻烦&#xff0c;pdf的内容位置都需要手动设置x&#xff08;横向&#xff09;和y&#xff08;纵向&#xff09;绝对位置&#xff…

PyTorch Tensor维度变换实战:view/squeeze/expand/repeat全解析

本文从图像数据处理、模型输入适配等实际场景出发&#xff0c;系统讲解PyTorch中view、squeeze、expand和repeat四大维度变换方法。通过代码演示对比不同方法的适用性&#xff0c;助您掌握数据维度调整的核心技巧。 一、基础维度操作方法 1. view&#xff1a;内存连续的形状重…

【NLP 面经 9、逐层分解Transformer】

目录 一、Transformer 整体结构 1.Tranformer的整体结构 2.Transformer的工作流程 二、Transformer的输入 1.单词 Embedding 2.位置 Embedding 计算公式&#xff1a; 三、Self-Attention 自注意力机制 1.Self-Attention 结构 ​编辑 2.Q、K、V的计算 代码实现 3.Self-Attenti…

这是一个文章标题

# Markdown 全语法示例手册本文档将全面演示 Markdown 的语法元素&#xff0c;包含 **标题**、**列表**、**代码块**、**表格**、**数学公式** 等 18 种核心功能。所有示例均附带实际应用场景说明。---## 一、基础文本格式### 1.1 标题层级 markdown # H1 (使用 #) ## H2 (使用…

xtrabackup备份

安装&#xff1a; https://downloads.percona.com/downloads/Percona-XtraBackup-8.0/Percona-XtraBackup-8.0.35-30/binary/tarball/percona-xtrabackup-8.0.35-30-Linux-x86_64.glibc2.17.tar.gz?_gl1*1ud2oby*_gcl_au*MTMyODM4NTk1NS4xNzM3MjUwNjQ2https://downloads.perc…

(51单片机)串口通讯(串口通讯教程)(串口接收发送教程)

前言&#xff1a; 今天有两个项目&#xff0c;分别为&#xff1a; 串口接收: 串口发送&#xff1a; 如上图将文件放在Keli5 中即可&#xff0c;然后烧录在单片机中就行了 烧录软件用的是STC-ISP&#xff0c;不知道怎么安装的可以去看江科大的视频&#xff1a; 【51单片机入门…

redis 延迟双删

Redis延迟双删是一种用于解决缓存与数据库数据一致性问题的策略&#xff0c;通常在高并发场景下使用。以下是其核心内容&#xff1a; 1. 问题背景 当更新数据库时&#xff0c;如果未及时删除或更新缓存&#xff0c;可能导致后续读请求仍从缓存中读取旧数据&#xff0c;造成数…

大语言模型中的幻觉现象深度解析

一、幻觉的定义及出现的原因 1. 基本定义 ​​幻觉(Hallucination)​​ 指大语言模型在自然语言处理过程中产生的与客观事实或既定输入相悖的响应&#xff0c;主要表现为信息失准与逻辑矛盾。 2. 幻觉类型与机制 2.1 事实性幻觉 ​​定义​​&#xff1a;生成内容与可验证…

详解如何从零用 Python复现类似 GPT-4o 的多模态模型

&#x1f9e0; 向所有学习者致敬&#xff01; “学习不是装满一桶水&#xff0c;而是点燃一把火。” —— 叶芝 我的博客主页&#xff1a; https://lizheng.blog.csdn.net &#x1f310; 欢迎点击加入AI人工智能社区&#xff01; &#x1f680; 让我们一起努力&#xff0c;共创…