【面试经典 150 | 分治】合并 K 个升序链表

文章目录

  • 写在前面
  • Tag
  • 题目来源
  • 解题思路
    • 方法一:顺序合并
    • 方法二:分治合并
    • 方法三:使用优先队列合并
  • 写在最后

写在前面

本专栏专注于分析与讲解【面试经典150】算法,两到三天更新一篇文章,欢迎催更……

专栏内容以分析题目为主,并附带一些对于本题涉及到的数据结构等内容进行回顾与总结,文章结构大致如下,部分内容会有增删:

  • Tag:介绍本题牵涉到的知识点、数据结构;
  • 题目来源:贴上题目的链接,方便大家查找题目并完成练习;
  • 题目解读:复述题目(确保自己真的理解题目意思),并强调一些题目重点信息;
  • 解题思路:介绍一些解题思路,每种解题思路包括思路讲解、实现代码以及复杂度分析;
  • 知识回忆:针对今天介绍的题目中的重点内容、数据结构进行回顾总结。

Tag

【链表】【分治】【优先队列】


题目来源

23. 合并 K 个升序链表


解题思路

在开始解题之前,需要先掌握 合并两个有序链表。

方法一:顺序合并

所谓的顺序合并就是将链表数组中的两个链表依次合并。思路比较简单清晰,直接看答案。

代码

/**
 * 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* merge2Lists(ListNode* list1, ListNode* list2){
        ListNode* dummy = new ListNode(-1);
        ListNode* prev = dummy;

        while(list1 && list2){
            if(list1->val < list2->val){
                prev->next = list1;
                list1 = list1->next;
            }
            else{
                prev->next = list2;
                list2 = list2->next;
            }
            prev = prev->next;
        }
        prev->next = list1 ? list1 : list2;

        return dummy->next;
    }
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        ListNode* ans = nullptr;
        int i;
        for(i = 0; i < lists.size(); ++i){
            ans = merge2Lists(ans, lists[i]);
        }
        return ans;
    }
};

复杂度分析

时间复杂度:渐进的时间复杂度为 O ( k 2 n ) O(k^2n) O(k2n)。分析。

空间复杂度: O ( 1 ) O(1) O(1)

方法二:分治合并

用分治的方法进行合并,步骤如下:

  • k 个链表配对并将同一对中的链表合并,
  • 第一轮合并以后, k 个链表被合并成了 k 2 \frac{k}{2} 2k 个链表,然后是 k 4 \frac{k}{4} 4k 个链表, k 8 \frac{k}{8} 8k 个链表等等;
  • 重复这一过程,直到我们得到了最终的有序链表。

代码

/**
 * 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* merge2Lists(ListNode* a, ListNode* b) {
        if (a == nullptr) {
            return b;
        }
        else if (b == nullptr) {
            return a;
        }
        else {
            if (a->val < b->val) {
                a->next = merge2Lists(a->next, b);
                return a;
            }
            else {
                b->next = merge2Lists(a, b->next);
                return b;
            }
        }
        return nullptr;         // 到不了这里
    }


    ListNode* merge(vector<ListNode*>& lists, int l, int r) {
        if (l == r) {
            return lists[l];
        }
        if (l > r) {
            return nullptr;
        } 

        int mid = l + ((r - l) >> 1);
        return merge2Lists(merge(lists, l, mid), merge(lists, mid+1, r));
    }

    ListNode* mergeKLists(vector<ListNode*>& lists) {
        return merge(lists, 0, lists.size() - 1);
    }
};

复杂度分析

时间复杂度:渐进的时间复杂度为 O ( k n × l o g k ) O(kn \times logk) O(kn×logk)。分析。

空间复杂度:递归会使用到 O ( log ⁡ k ) O(\log k) O(logk) 空间代价的栈空间。

方法三:使用优先队列合并

思路

维护一个优先队列(小顶堆),队列中存放的是 pair<int, ListNode*> 其中第一个元素表示的是节点的值,第二个元素表示该节点,优先队列是关于 节点的值 的优先队列。优先队列中也可以放置封装了节点的值和节点的结构体。小顶堆可以保证每次出队的都是队列中的最小元素,然后将出队的最小元素连接到链表中。

代码

/**
 * 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:
    struct Status {
        int val;
        ListNode *node;
        bool operator < (const Status& rhs) const {
            return val > rhs.val;
        }
    };
    
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        priority_queue<Status> que;
        for (auto node : lists) {
            if (node) {
                que.push({node->val, node});
            }
        }

        ListNode head, *tail = &head;           // 见笔记中代码理解

        while (!que.empty()) {
            auto f = que.top(); que.pop();
            tail->next = f.node;
            tail = tail->next;
            if (f.node->next) {
                que.push({f.node->next->val, f.node->next});
            }
        }
        return head.next;
    }
};

理解 ListNode head, *tail = &head;

head 初始化为一个空节点,最后 head.next 指向的是合并后链表的头结点。通过 tail 指针来帮助 head.next 指向合并后链表的头节点。

tail 是一个指针,初始化指向的是 head。在第一次为 tail 建立后继节点时,就将捆绑的 head 也指向了一个节点(就是合并链表后的头节点),接着通过 tail = tail->next 操作将 tail 指向了新的节点,也就切断了 tailhead 的联系。最后返回合并后的链表头节点为 head->next

复杂度分析

时间复杂度:考虑优先队列中的元素不超过 k 个,那么插入和删除的时间代价为 O ( l o g ⁡ k ) O(log⁡k) O(logk),这里最多有 kn 个点,对于每个点都被插入删除各一次,故总的时间代价即渐进时间复杂度为 O ( k n × log ⁡ k ) O(kn \times \log k) O(kn×logk)

空间复杂度:这里用了优先队列,优先队列中的元素不超过 k 个,故渐进空间复杂度为 O ( k ) O(k) O(k)


写在最后

如果您发现文章有任何错误或者对文章有任何疑问,欢迎私信博主或者在评论区指出 💬💬💬。

如果大家有更优的时间、空间复杂度的方法,欢迎评论区交流。

最后,感谢您的阅读,如果有所收获的话可以给我点一个 👍 哦。

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

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

相关文章

知识图谱推动条件

文章目录 计算设备及硬件的发展可用数据规模的提升算法演进数据/知识检索需求攀升开源知识库建设专业人才培养 计算设备及硬件的发展 知识图谱的发展离不开计算硬件的支撑&#xff0c;特别是知识图谱构建、推理、应用过程中的机器学习算法的训练和预测等过程&#xff0c;对计算…

XYCTF2024 RE ez unity 复现

dll依然有加壳 但是这次global-metadata.dat也加密了&#xff0c;原工具没办法用了&#xff0c;不过依然是可以修复的 a. 法一&#xff1a;frida-il2cpp-bridge 可以用frida-il2cpp-bridge GitHub - vfsfitvnm/frida-il2cpp-bridge: A Frida module to dump, trace or hijac…

Docker搭建LNMP+Wordpress的实验

目录 一、项目的介绍 1、项目需求 2、服务器环境 3、任务需求 二、Linux系统基础镜像 三、部署Nginx 1、建立工作目录 2、编写Dockerfile 3、准备nginx.conf配置文件 4、设置自定义网段和创建镜像和容器 5、启动镜像容器 6、验证nginx 三、Mysql 1、建立工作目录…

如何在CentOS使用Docker运行Nacos容器并实现无公网IP远程访问UI界面

文章目录 1. Docker 运行Nacos2. 本地访问Nacos3. Linux安装Cpolar4. 配置Nacos UI界面公网地址5. 远程访问 Nacos UI界面6. 固定Nacos UI界面公网地址7. 固定地址访问Plik Nacos是阿里开放的一款中间件,也是一款服务注册中心&#xff0c;它主要提供三种功能&#xff1a;持久化…

nginx--压缩https证书favicon.iconginx隐藏版本号 去掉nginxopenSSL

压缩功能 简介 Nginx⽀持对指定类型的⽂件进行压缩然后再传输给客户端&#xff0c;而且压缩还可以设置压缩比例&#xff0c;压缩后的文件大小将比源文件显著变小&#xff0c;这样有助于降低出口带宽的利用率&#xff0c;降低企业的IT支出&#xff0c;不过会占用相应的CPU资源…

[leetcode]Z 字形变换

. - 力扣&#xff08;LeetCode&#xff09; class Solution { public:string convert(string s, int numRows) {int n s.length(), r numRows;if (r 1 || r > n) {return s;}int t r * 2 - 2;int c (n t - 1) / t * (r - 1);vector<string> mat(r, string(c, 0)…

第Y9周:重要模块解读

&#x1f368; 本文为&#x1f517;365天深度学习训练营 中的学习记录博客&#x1f356; 原作者&#xff1a;K同学啊 | 接辅导、项目定制&#x1f680; 文章来源&#xff1a;K同学的学习圈子 目录 以con.py为例&#xff1a; 一、autopad 二、Conv 三、Focus 四、C2f 文件…

【牛客网】值周

原题链接&#xff1a;登录—专业IT笔试面试备考平台_牛客网 目录 1. 题目描述 2. 思路分析 3. 代码实现 1. 题目描述 2. 思路分析 差分。 因为l<100000000,所以数组开1e8。 唯一需要注意的点就是前面给b[0]单独赋值为1&#xff08;因为如果在循环中给b[0]赋值&…

Linux PTP学习

前言 本文是对Linux PTP的学习记录&#xff0c;不足之处请指出。Linux PTP用于在Linux系统的精确时钟同步&#xff0c;支持IEEE 1588 Precision Time Protocol&#xff08;PTP&#xff09;标准&#xff0c;目的是实现在网络中&#xff0c;设备之间的高精度时间同步。它是一个工…

SSM整合-前后端分离-项目环境搭建 (上)

整合SSM 项目基础环境搭建项目介绍创建项目项目全局配置web.xmlSpringMVC配置配置Spring和MyBatis, 并完成整合创建表, 使用逆向工程生成Bean, XxxMapper和XxxMapper.xml注意事项和细节说明 实现功能01-搭建Vue前端工程需求分析/图解代码实现搭建Vue前端工程vue3项目目录结构梳…

Pytorch学习笔记——TensorBoard的初使用

1、TensorBoard介绍 TensorBoard是TensorFlow的可视化工具&#xff0c;但它也可以与PyTorch结合使用。TensorBoard提供了一个Web界面&#xff0c;可以展示你训练过程中的各种信息&#xff0c;如损失值、准确度、权重分布等&#xff0c;更好地帮助开发者理解和调试模型。 Tenso…

01 Activiti 7:步骤

01 Activiti 7&#xff1a;步骤 1. 整合Activiti2. 业务流程建模3. 部署业务流程4. 启动流程实例5. 查询待办任务6. 处理待办任务7. 结束流程 1. 整合Activiti 业务系统使用 Activiti 来对系统的业务流程进行自动化管理。为了方便业务系统访问&#xff08;操作&#xff09;Act…

看了这一篇,你不用再为找oracle安装介质发愁了!

写这篇文章的原因是&#xff1a;经常有49年还想要入国军学习Oracle的小伙伴问要不同版本的Oracle软件安装包&#xff08;说明一下&#xff0c;尊重版权&#xff0c;拒绝盗版&#xff0c;还是要从正规渠道获得介质&#xff09; 事实上很多人遇到过样的坑&#xff0c;才发现正规…

YH11047A 三串四串锂电保护板的3串使用问题 8254A电池芯片

网上的示例电路4串正确&#xff0c;但是3串错误 我使用3串接线时&#xff0c;pp-电压只有0.几v。即被保护 根据查询8254A的IC资料 发现3串和4串的电路图有明显区别&#xff1a; 1、3串的sel脚接vss&#xff08;低电势&#xff09;&#xff0c;4串的sel脚接vdd&#xff08;高电…

Python AI 速成课:快速打造高效AI

这个文章主要是对python AI 小白的 。实用性很强&#xff0c;不用太多复杂步骤。 第一步&#xff1a;先到Try NVIDIA NIM APIs这个网站 然后使用邮箱注册&#xff0c;很简单和快捷。 第二步&#xff1a;点击微软的phi-3模型&#xff0c;这个API是免费的 第三步&#xff1a; 获取…

00 Activiti 7:介绍

00 Activiti 7&#xff1a;介绍 1. 前言2. 介绍3. 官网4. 核心机制5. BPMN5.1. 核心要素5.1.1. 流程元素5.1.2. 连接元素5.1.3. 数据和消息5.1.4. 协作 1. 前言 工作流&#xff08;Workflow&#xff09;是一种管理和自动化业务过程的方法&#xff0c;它将一系列任务或活动按照…

117篇 | 3D Gaussian Splatting论文

本论文集划分为4个部分&#xff1a;综述&基础&#xff08;14篇&#xff09;、NeRF在AIGC&#xff08;54篇&#xff09;、NeRF在SLAM&#xff08;自动驾驶&#xff09;&#xff08;25篇&#xff09;、NeRF之场景建模&#xff08;25篇&#xff09; https://t.zsxq.com/3ATyE…

大气官网(1):家居家电,海量案例来袭。

设计一款大气的家居家电官网&#xff0c;可以考虑以下几个方面&#xff1a; 色彩选择&#xff1a;选择适合家居家电风格的色彩搭配。可以选择温暖的中性色调&#xff0c;如米白色、灰色和棕色&#xff0c;以增加页面的大气感和舒适感。图片展示&#xff1a;使用高质量的图片展…

京东JD商品SKU信息API返回值解析:精准掌握商品属性

在电子商务迅猛发展的今天&#xff0c;商家对于商品信息的掌握和管理显得尤为重要。作为电商平台的佼佼者&#xff0c;京东&#xff08;JD&#xff09;提供了丰富的API接口&#xff0c;使得商家能够轻松地获取商品的详细信息&#xff0c;包括SKU&#xff08;Stock Keeping Unit…

ESP32 烧录固件

第一步&#xff1a;下载固件 git clone --recursive https://github.com/espressif/esp-at.git 第二步&#xff1a;执行编译 在该目录执行 python build.py install 如图&#xff1a; 第三步&#xff1a;选择芯片 输入2 第四步&#xff1a;选择固件 输入1 第五步&#…