力扣hot100:23. 合并 K 个升序链表

23. 合并 K 个升序链表
在这里插入图片描述
这题非常容易想到归并排序的思路,俩升序序列合并,可以使用归并的方法。

不过这里显然是一个多路归并排序;包含多个子数组的归并算法,这可以让我们拓展归并算法的思路。

假设n是序列个数,ni是单个序列长度,length是单个序列最大长度

1、顺序单次归并

从左往右依次进行归并,但是这种方法存在一定的缺点。假设n是序列个数,ni是单个序列长度,根据题设,这个方法的最大比较次数至少是:
n 1 + ( n 1 + n 2 ) + ( n 1 + n 2 + n 3 ) ⋅ ⋅ ⋅ = n ∗ n 1 + ( n − 1 ) ∗ n 2 + ( n − 2 ) ∗ n 3 ⋅ ⋅ ⋅ < = n ∗ ( n 1 + n 2 + n 3 + ⋅ ⋅ ⋅ ) = n 2 ∗ l e n g t h < = 1 0 4 ∗ 500 ∗ 1 0 4 n1+(n1+n2)+(n1+n2+n3)··· = n*n1 + (n-1)*n2 + (n-2)*n3··· <= n*(n1+n2+n3+···) = n^2 * length <=10^4*500*10^4 n1+(n1+n2)+(n1+n2+n3)⋅⋅⋅=nn1+(n1)n2+(n2)n3⋅⋅⋅<=n(n1+n2+n3+⋅⋅⋅)=n2length<=104500104
这相当于每个序列都需要被比较序列个数次,这换成是多路归并的数组合并也是一样的。

官方:
在这里插入图片描述
在这里插入图片描述

class Solution {
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        ListNode * head = nullptr;
        for(int i = lists.size() - 1;i >= 0; --i){
            head = merge(head, lists[i]);
            ListNode * temp = head;
        }
        return head;
    }
private:
    ListNode * merge(ListNode * p,ListNode * q){
        if(!p) return q;
        if(!q) return p;
        ListNode * head = p;
        if(head->val > q->val) head = q;
        if(head == p) p = p->next;
        else q = q->next;

        ListNode * temp = head;
        while(p && q){
            if(p->val > q->val){
                head->next = q;
                q = q->next;
            }else{
                head->next = p;
                p = p->next;
            }
            head = head->next;
        }
        head->next = p ? p : q;
        return temp;
    }
};

2、分治归并

使用分治的思想排序是很容易想到的,但是不能很容易的知道分治归并速度一定更快,接下来让我详细思考一下是否会更快:
我们可以考虑,每次将序列数量减半合并,那么每一层合并使用的时间是 O ( l e n g t h ∗ n ) O(length*n) O(lengthn),我们知道每层数量减半,那么一共是有 O ( l o g n ) O(logn) O(logn)层,所以时间复杂度为 O ( n l o g n ∗ l e n g t h ) O(nlogn * length) O(nlognlength)

为什么分治归并会比普通顺次归并要快?
可以这样看一下,使用分治,将所有数分为两个区间[l,mid][mid+1,r],左区间的数 和 右区间的数只会在最后合并时比较一次,其他时候打死不相往来。而使用顺序归并,左区间的数 和 右区间的数会比较很多次,在考虑到
在这里插入图片描述
在这里插入图片描述

class Solution {
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        if(lists.size() == 0) return nullptr;
        return mergesort(lists, 0, lists.size() - 1);
    }
private:
    ListNode * merge(ListNode * p,ListNode * q){
        if(!p) return q;
        if(!q) return p;
        ListNode * head = p;
        if(head->val > q->val) head = q;
        if(head == p) p = p->next;
        else q = q->next;

        ListNode * temp = head;
        while(p && q){
            if(p->val > q->val){
                head->next = q;
                q = q->next;
            }else{
                head->next = p;
                p = p->next;
            }
            head = head->next;
        }
        head->next = p ? p : q;
        return temp;
    }
    ListNode * mergesort(vector<ListNode*>& lists,int left,int right){
        if(left == right) return lists[left];
        int mid = (left + right) >> 1;
        ListNode * p = mergesort(lists,left,mid);
        ListNode * q = mergesort(lists,mid + 1, right);
        return merge(p, q);
    }
};

3、使用优先队列合并

这种方式非常牛。

我们将所有序列,依据序列头部的元素大小放入一个优先队列,那么这个优先队列的深度是 l o g n logn logn,然而我们每次取出一个结点它的头部必然是现在里面最小的,将它放入待合并的目标序列中,然后将该序列后移一位,插入到优先队列中。因此每个元素插入时间是 O ( l o g n ) O(logn) O(logn),一共有 ( l e n g t h ∗ n ) (length * n) (lengthn)个元素。所以总时间为 O ( n l o g n ∗ l e n g t h ) O(nlogn*length) O(nlognlength)
在这里插入图片描述

class Solution {
public:
    ListNode* mergeKLists(vector<ListNode*>& lists) {
        if(lists.size() == 0) return nullptr;
        priority_queue<ListNode *,vector<ListNode *>,Sort> q;
        for(int i = lists.size() - 1;i >= 0; --i) if(lists[i]) q.push(lists[i]);
        ListNode * dummy = new ListNode;
        ListNode * temp = dummy;

        while(!q.empty()){
            ListNode * head = q.top();q.pop();
            temp->next = head;
            temp = temp->next;
            head = head->next;
            if(head) q.push(head);
        }
        temp = dummy->next;
        delete dummy;
        return temp;
    }
private:
    struct Sort{
        bool operator ()(const ListNode * a,const ListNode * b){
            return a->val > b->val;
        }
    };
};

能够实现优先队列,那么这个问题就很容易被解决。

  • 我们需要注意两个问题
    • 优先队列使用的比较函数必须自定义为结构体或者符号重载
    • 优先队列使用的比较函数的大于号小于号取值,和sort刚好相反。

使用方式:

priority_queue<Exp,vector<Exp>,cmp> q;
struct cmp{
	bool operator() (Exp a, Exp b){
       if() return true;
       return false;
    }
}

唯一变化的就是括号里面的类型Exp和你想要定义的比较方式。

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

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

相关文章

这么多不同接口的固态硬盘,你选对了嘛!

固态硬盘大家都不陌生,玩游戏、办公存储都会用到。如果自己想要给电脑或笔记本升级下存储,想要存储更多的文件,该怎么选购不同类型的SSD固态盘呐,下面就来认识下日常使用中常见的固态硬盘。 固态硬盘(Solid State Drive, SSD)作为数据存储技术的革新力量,其接口类型的选…

5.25.6 深度学习在放射图像中检测和分类乳腺癌病变

计算机辅助诊断 (CAD) 系统使用数字化乳房 X 线摄影图像并识别乳房中存在的异常情况。深度学习方法从有限数量的专家注释数据中学习图像特征并预测必要的对象。卷积神经网络&#xff08;CNN&#xff09;在图像检测、识别和分类等各种图像分析任务中的性能近年来表现出色。本文提…

VSCode连接远程服务器使用jupyter报错问题解决

目录 一. 问题描述二. jupyter环境确认三. 插件安装 一. 问题描述 经常会遇到一种问题就是, VSCode连接远程服务器, 上次jupyter notebook 还用的好好的, 下次打开就显示找不到内核了. 今天提供了全套解决方案, 帮大家迅速解决环境问题. 二. jupyter环境确认 首先进入自己需…

OPPO Reno12系列发布:用它玩游戏比凉茶还要“凉”

在这个智能手机市场日新月异的时代&#xff0c;每一次新品发布都牵动着无数科技爱好者的心。最近&#xff0c;OPPO官微传来好消息&#xff0c;即将推出的OPPO Reno12系列不仅搭载了顶尖的旗舰芯片&#xff0c;还与联发科天玑强强联手&#xff0c;进行了深度的优化调校&#xff…

【408真题】2009-21

“接”是针对题目进行必要的分析&#xff0c;比较简略&#xff1b; “化”是对题目中所涉及到的知识点进行详细解释&#xff1b; “发”是对此题型的解题套路总结&#xff0c;并结合历年真题或者典型例题进行运用。 涉及到的知识全部来源于王道各科教材&#xff08;2025版&…

Honor of Kings 2024.03.29 Ban for 3 day

我又被举报消极然后禁赛 都说了别选蔡文姬&#xff0c;对面三个肉&#xff0c;非要选个软辅助 吐槽下这游戏策划&#xff1a;游戏体验感越来越差&#xff0c;公正也很差 对说了对面4个法师&#xff0c;就是不出魔抗&#xff0c;把把都是0-N开局&#xff0c;到底谁消极啊&#x…

apexcharts数据可视化之圆环柱状图

apexcharts数据可视化之圆环柱状图 有完整配套的Python后端代码。 本教程主要会介绍如下图形绘制方式&#xff1a; 基础圆环柱状图多组数据圆环柱状图图片背景自定义角度渐变半个圆环图虚线圆环图 基础圆环图 import ApexChart from react-apexcharts;export function Cir…

基于jeecgboot-vue3的Flowable流程-我的任务(一)

因为这个项目license问题无法开源&#xff0c;更多技术支持与服务请加入我的知识星球。 1、首先可以用现成生成代码的前端来做这个&#xff0c;只要做一些调整就可以了&#xff0c;这样利用现有的一些模板可以快速构建我的任务&#xff0c;否则vue2与vue3相差太大&#xff0c;移…

将文件批量重命名001到100?怎么批量修改文件夹名字?这四款工具不要错过!

你们有没有遇到过需要批量修改文件&#xff08;文件夹&#xff09;名的情况&#xff1f;从网上下载一些文件都会带有一些后缀名字。大量的文件&#xff0c;一个一个修改重命名的话&#xff0c;这简直是个头疼的事情。市面上虽然有很多批量文件重命名工具&#xff0c;但要么收费…

memblock_free_all释放page到buddy,前后nr_free的情况

https://www.cnblogs.com/tolimit/p/5287801.html 在zone_sizes_init 之后&#xff0c;各个node&#xff0c;zone的page总数已知。但是此时的每个order的空闲链表是空的&#xff0c;也就是无法通过alloc_page这种接口来分配。此时page还在memblock管控&#xff0c;需要memblock…

IT人的拖延——别让“对失败的担忧”吓跑了“幸福感”

除了完美主义情结外&#xff0c;拖延的另一大重要原因是“对于失败的担忧”&#xff0c;当我们尝试没有把握的事&#xff0c;或者是曾经做这件事失败过&#xff0c;再次需要尝试时&#xff0c;因为对自我有“成功”的期望&#xff0c;自然就会担忧失败的可能性。比如&#xff0…

前端Vue自定义个性化导航栏菜单组件的设计与实现

摘要&#xff1a; 随着前端技术的飞速发展和业务场景的日益复杂&#xff0c;组件化开发已成为提升开发效率和降低维护成本的关键手段。本文将以Vue uni-app平台为例&#xff0c;介绍如何通过自定义导航栏菜单组件&#xff0c;实现业务逻辑与界面展示的解耦&#xff0c;以及如何…

基于 Coze 从 0-1 搭建专属 小白的Bot 机器人

基于 Coze 从 0-1 搭建专属 小白的Bot 机器人 ​ 作为一个GIS从业人员&#xff0c;对于AI的使用是必不可少的&#xff0c;在过去的一两年里各种大模型频出&#xff0c;AI技术已经成为GIS领域的一项重要工具&#xff0c;为我们提供了许多强大的功能和解决方案。看到好文章都在介…

在PyCharm中,不希望新建Python文件自动打开Python控制台

很久没更新水一下 第一步编辑配置 第二步编辑配置模板 第三步取消勾选 第四步确定

【贪心算法题记录】376. 摆动序列

题目链接 题目描述 如果连续数字之间的差严格地在正数和负数之间交替&#xff0c;则数字序列称为 摆动序列 。第一个差&#xff08;如果存在的话&#xff09;可能是正数或负数。仅有一个元素或者含两个不等元素的序列也视作摆动序列。 例如&#xff0c; [1, 7, 4, 9, 2, 5] …

【Qt】Qt框架文件处理精要:API解析与应用实例:QFile

文章目录 前言&#xff1a;1. Qt 文件概述2. 输入输出设备类3. 文件读写类3.1. 打开open3.2. 读read / readline/ readAll3.3. 写write3.4. 关闭close 4. 读写文件示例5. 文件件和目录信息类总结&#xff1a; 前言&#xff1a; 在现代软件开发中&#xff0c;文件操作是应用程序…

好消息!DolphinScheduler官网集成LLM模型问答AI kapa.ai

不少小伙伴可能发现了&#xff0c;Apache DolphinScheduler官网最近默默上线了kapa.ai作为LLM的问答AI。 集成kapa.ai之后&#xff0c;社区用户可以点击Apache DolphinScheduler官网首页右下角的「Ask AI」模块&#xff0c;在接下来弹出的问答框输入自己的问题&#xff0c;即可…

uniapp通过Canvas绘制网格(心电图,坐标纸等可用)

本篇文档是Canvas绘制心电图的第一个部分&#xff0c;想了解详情的可以关注后学习交流。 心电图的最底层需要一个网状底层&#xff0c;来方便进行数据的测量。 一、白底分大、中、小三个区域的网格 1、首先是HTML部分 <!DOCTYPE html> <html lang"en">…

睿联技术对亚马逊既依赖又竞争:递表前大额分红,资金充裕又补流?

《港湾商业观察》施子夫 王璐 今年3月29日&#xff0c;冲刺创业板IPO的深圳市睿联技术股份有限公司&#xff08;以下简称&#xff0c;睿联技术&#xff09;提交了注册&#xff0c;不出意外的话&#xff0c;公司离挂牌上市已经近在咫尺。 然而&#xff0c;在目前资本市场尤其…

HNU-计算机体系结构-期末复习

前言 这是新开的课程&#xff0c;故历年考题有限。2024年期末考试的情况像大默写。期末试卷回忆在这里&#xff1a; 计算机体系结构-2024期末考试-CSDN博客 不知道结果怎么样&#xff0c;希望别太对不起付出吧。 资源推荐 本着不重复造轮子的原则&#xff0c;这里推荐学长以…