【力扣hot100】23 合并K个排序链表(c++)解析

23 合并K个排序链表(c++)解析

题解

  • 23 合并K个排序链表(c++)解析
      • 1.1 暴力求解
      • 1.2 逐一比较
      • 1.3 优先队列
      • 1.4 逐一合并
      • 1.5 分治

在解决「合并K个排序链表」这个问题之前,我们先来看一个更简单的问题:

如何合并两个有序链表?

假设链表a和b的长度都是 n,如何在 O(n)的时间代价以及 O(1)的空间代价完成合并?

这个问题在面试中常常出现,为了达到空间代价是 O(1),我们的宗旨是「原地调整链表元素的next 指针完成合并」。

首先我们需要一个变量 head 来保存合并之后链表的头部,你可以把 head 设置为一个虚拟的头(也就是head 的 val属性不保存任何值),这是为了方便代码的书写,在整个链表合并完之后,返回它的下一位置即可。我们需要一个指针 tail 来记录下一个插入位置的前一个位置,以及两个指针 aPtr和 bPtr 来记录 a和b未能合并部分的第一位。注意这里的描述,tail不是下一个插入的位置,aPtr和 bPtr所指向的元素处于「待合并」的状态,也就是说它们还没有合并入最终的链表。当然你也可以给他们赋予其他的定义,但是定义不同实现就会不同。

当 aPtr 和 bPtr 都不为空的时候,取 val 属性较小的合并;
如果 aPtr为空,则把整个 bPtr 以及后面的元素全部合并;
bPtr 为空时同理。

在合并的时候,应该先调整 tail的 next 属性,再后移tail和*Ptr(aPtr 或者 bPtr)。

那么这里 tail 和*Ptr是否存在先后顺序呢?

它们谁先动谁后动都是一样的,不会改变任何元素的 next 指针。

ListNode* mergeTwoLists(ListNode *a, ListNode *b) {if ((!a) || (!b)) return a ? a : b;     // 如果其中一个输入链表为空,则直接返回另一个链表 如果 a 不为空,则返回 a,否则返回 b//ListNode head 声明了一个名为 head 的局部变量,类型为 ListNode。在这个声明中,head 并不是一个指针,而是一个对象或者说是一个节点。//一个指针 `tail`,将其指向 `head` 指针所指向的地址ListNode head, *tail = &head, *aPtr = a, *bPtr = b; // 初始化一个虚拟头结点和一个尾指针 定义用于遍历输入链表的指针while (aPtr && bPtr) {if (aPtr->val < bPtr->val) {tail->next = aPtr; aPtr = aPtr->next; // 如果链表 'a' 的值较小,则将其追加到合并后的链表中  aPtr移动到链表 'a' 的下一个节点} else {tail->next = bPtr; bPtr = bPtr->next;}tail = tail->next;//将尾指针移动到合并后的链表的最后一个节点}tail->next = (aPtr ? aPtr : bPtr); // 将剩余的节点(如果有)追加到合并后的链表中return head.next;// 返回合并后的链表的起始节点(不包括虚拟头结点)
}

复杂度分析
时间复杂度:O(n)。
空间复杂度:O(1)。

1.1 暴力求解

在这里插入图片描述

在这里插入图片描述
暴力解法就不演示了,自己可以尝试一下

1.2 逐一比较

在这里插入图片描述
在这里插入图片描述
不如直接看1.3优先队列解法

1.3 优先队列

在这里插入图片描述
对于优先队列有这几种做法:

方法1:建立优先队列(最大堆或者最小堆均可),全部元素接连入队; 最后再不断弹出,构建链表。这也是一种想法,不过这样子效率就有些低下了。
方法2: 依然建立优先队列,不过不需要全部元素一次性入队;只需要让链表头元素入队即可,弹出该元素后,该链表往后移。

使用小根堆(优先队列)合并 K 个有序链表
在这里插入图片描述
在这里插入图片描述

class Solution {
public:// 小根堆的回调函数struct cmp{ //定义了一个函数对象bool operator()(ListNode *a,ListNode *b){//重载了小根堆的比较操作符,用于比较两个节点的值return a->val > b->val;//大的值先被放入尾部,自然小的值就会在顶部}};ListNode* mergeKLists(vector<ListNode*>& lists) {//存储元素类型,整体类型,以及小根堆定义priority_queue<ListNode*, vector<ListNode*>, cmp> pri_queue;// 建立大小为k的小根堆,声明了一个优先队列 pri_queue,它存储的元素类型是 ListNode*,表示指向链表节点的指针。//重载了小根堆的比较操作符的目的是为了定义优先队列中节点的比较规则,默认情况下,优先队列使用的是 < 操作符进行比较,即比较节点指针的大小;但在这个场景下,我们需要根据节点的值来进行比较,而不是比较节点指针的大小//使用了上面定义的 cmp 函数对象来比较节点的值。这样定义的效果是,队首元素是值最小的节点。for(auto elem : lists){if(elem) pri_queue.push(elem);//将所有非空的链表头节点(elem)依次加入小根堆}// 可以使用哑节点/哨兵节点ListNode dummy(-1);ListNode* p = &dummy;//这个哑节点用于简化链表的操作,我们将通过 p 指针来不断连接合并后的链表// 开始出队while(!pri_queue.empty()){ListNode* top = pri_queue.top(); pri_queue.pop();p->next = top; p = p->next;//每次取出堆顶元素(值最小的节点)if(top->next) pri_queue.push(top->next);//被取出的下一个节点入队}return dummy.next;  }
};

1.4 逐一合并

在这里插入图片描述
代码如下:

class Solution {
public:ListNode* mergeTwoLists(ListNode *a, ListNode *b) {if ((!a) || (!b)) return a ? a : b; //检查 `a` 和 `b` 是否为空,如果其中一个为空,直接返回另一个链表ListNode head, *tail = &head, *aPtr = a, *bPtr = b;//它采用了迭代的方法,使用了三个指针 `tail`、`aPtr` 和 `bPtr`,以及一个虚拟头结点 `head`while (aPtr && bPtr) {if (aPtr->val < bPtr->val) {tail->next = aPtr; aPtr = aPtr->next;} else {tail->next = bPtr; bPtr = bPtr->next;}tail = tail->next;}tail->next = (aPtr ? aPtr : bPtr);return head.next;}ListNode* mergeKLists(vector<ListNode*>& lists) { //函数用于合并给定的 K 个有序链表,存储在 `lists` 向量中ListNode *ans = nullptr;//size_t 是 C++ 中的一种无符号整数类型,通常用于表示数组索引、大小和长度等非负整数值for (size_t i = 0; i < lists.size(); ++i) {ans = mergeTwoLists(ans, lists[i]);//通过多次调用 `mergeTwoLists` 函数来逐个合并链表,最终得到一个合并后的链表。}return ans;}
};

1.5 分治

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

class Solution{
public:ListNode* mergeTwoLists(ListNode*a, ListNode*b){if((!a)||(!b)) return a? a: b;ListNode head, *tail =&head, *aPtr = a, *bPtr = b;      while(aPtr && bPtr){if(aPtr->val<bPtr->val){tail->next = aPtr; aPtr = aPtr ->next;}else if(aPtr->val>= bPtr->val){tail ->next = bPtr; bPtr = bPtr ->next;}tail = tail->next;}if((!aPtr)||(!bPtr)){tail->next = (aPtr?aPtr:bPtr);}return head.next;}//l 和 r 表示当前要处理的链表范围的左右边界。ListNode* merge(vector<ListNode*> &lists, int l ,int r){if(l == r) return lists[l];//如果左右边界相等,说明只有一个链表需要处理,直接返回该链表的头节点。if(l>r) return nullptr;//如果左边界大于右边界,则返回空指针int mid = (l+r)>>1;//二进制数进行右移操作,相当于除以2 return mergeTwoLists(merge(Lists, l, mid), merge(lists, mid+1,r));//递归地将左右两个子范围的链表进行合并,递归到最后比如当1=mid 或者mid+1=r时直接返回lists}ListNode* mergeKLists(vector<ListNode*> &lists){return merge(lists, 0, lists.size()-1);}};

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

如若内容造成侵权/违法违规/事实不符,请联系长河编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

libVLC 动态视频壁纸

在 Windows 上&#xff0c;你可能需要使用 Windows API 来设置壁纸&#xff0c;而在 Linux 上&#xff0c;你可能需要使用某种桌面环境特有的方法。在 macOS 上&#xff0c;这一功能可能受到限制。 效果图如下所示&#xff1a; 以下是一个简单的示例&#xff0c;说明了如何在 …

【前端学习——js篇】11.元素可见区域

具体见&#xff1a;https://github.com/febobo/web-interview 11.元素可见区域 ①offsetTop、scrollTop offsetTop&#xff0c;元素的上外边框至包含元素的上内边框之间的像素距离&#xff0c;其他offset属性如下图所示&#xff1a; 下面再来了解下clientWidth、clientHeight…

文章解读与仿真程序复现思路——中国电机工程学报EI\CSCD\北大核心《考虑长周期供需不平衡风险的新型电力系统规划方法》

本专栏栏目提供文章与程序复现思路&#xff0c;具体已有的论文与论文源程序可翻阅本博主免费的专栏栏目《论文与完整程序》 论文与完整源程序_电网论文源程序的博客-CSDN博客https://blog.csdn.net/liang674027206/category_12531414.html 电网论文源程序-CSDN博客电网论文源…

codesys通过moudbus TCP连接西门子1214c,西门子做客户端

思路在codesys中发送数据到西门子&#xff0c;西门子原封不动的将数据传回。 1.首先配置codesys; 我设置了500个&#xff0c;但是好像发不这么多&#xff0c;只能120多个。因为什么来我忘了。但是这里不影响。 2.配置映射&#xff1a; 3.写代码 PROGRAM PLC_PRG VARarySendDa…

生产调度问题分类——机器视角

获取更多资讯&#xff0c;赶快关注上面的公众号吧&#xff01; 文章目录 单机调度并行机调度流水车间调度作业车间调度柔性作业车间开放车间总结 生产调度问题是实际工作中广泛存在的运筹学问题&#xff0c;可以描述为“给定若干加工任务&#xff0c;根据已有的生产条件&#…

JavaWeb学习笔记01

一、教程简介 全新JAVAWEB&#xff08;里程碑版&#xff09; 一套更适合后端工程师学习的WEB教程 All in Java 1、后端 ① Spring全家桶及微服务框架 ② 高性能数据库和消息组件 ③ Web攻击防护安全控制手段 ④ 其他第三方SDK生态环境 ...... 2、前端 ① 视图三大件&…

C是用什么语言写出来的?

C是用什么语言写出来的? C语言的起源和发展是一个迭代过程&#xff1a; 1. 最初的C语言编译器的开发始于对B语言的改进。B语言是由Ken Thompson设计的&#xff0c;它是基于BCPL语言简化而来的。在开始前我有一些资料&#xff0c;是我根据网友给的问题精心整理了一份「 C语言的…

Sublime 彻底解决中文乱码

1. 按ctrl&#xff0c;打开Console&#xff0c;输入如下代码&#xff1a; import urllib.request,os; pf Package Control.sublime-package; ipp sublime.installed_packages_path(); urllib.request.install_opener( urllib.request.build_opener( urllib.request.ProxyHand…

16:00面试,16:06就出来了,问的问题有点变态。。。

从小厂出来&#xff0c;没想到在另一家公司又寄了。 到这家公司开始上班&#xff0c;加班是每天必不可少的&#xff0c;看在钱给的比较多的份上&#xff0c;就不太计较了。没想到8月一纸通知&#xff0c;所有人不准加班&#xff0c;加班费不仅没有了&#xff0c;薪资还要降40%…

07_Response

文章目录 案例&#xff08;请求分发案例&#xff09; Response响应行响应头响应体特殊响应头refreshContent-typeContent-dispositionlocation 案例&#xff08;登录案例&#xff09; 案例&#xff08;请求分发案例&#xff09; 场景&#xff1a;有多个请求 Http://localhost:…

逐步学习Go-并发通道chan(channel)

概述 Go的Routines并发模型是基于CSP&#xff0c;如果你看过七周七并发&#xff0c;那么你应该了解。 什么是CSP&#xff1f; "Communicating Sequential Processes"&#xff08;CSP&#xff09;这个词组的含义来自其英文直译以及在计算机科学中的使用环境。 CSP…

day 36 贪心算法 part05● 435. 无重叠区间 ● 763.划分字母区间 ● 56. 合并区间

一遍过。首先把区间按左端点排序&#xff0c;然后右端点有两种情况。 假设是a区间&#xff0c;b区间。。。这样排列的顺序&#xff0c;那么 假设a[1]>b[0],如果a[1]>b[1]&#xff0c;就应该以b[1]为准&#xff0c;否则以a[1]为准。 class Solution { public:static bo…

Android中运动事件的处理

1.目录 目录 1.目录 2.前言 3.程序演示 4.第二种程序示例 5.扩展 2.前言 触摸屏&#xff08;TouchScreen&#xff09;和滚动球&#xff08;TrackBall&#xff09;是 Android 中除了键盘之外的主要输入设备。如果需要使用触摸屏和滚动球&#xff0c;主要可以通过使用运动事…

渐变色x轴换行柱状图

// 系统上云率const optionBar {title: {text: 系统上云率,left: left,textStyle: {color: "#fff",fontSize: 14,fontWeight: 650,align: "center",},},color: [#32C5FF, #00F766, #EECB5F],grid: {top: 40,bottom: 0,},legend: { // 控制图例组件show: …

K8s Pod亲和性、污点、容忍度、生命周期与健康探测详解(上)

&#x1f407;明明跟你说过&#xff1a;个人主页 &#x1f3c5;个人专栏&#xff1a;《Kubernetes航线图&#xff1a;从船长到K8s掌舵者》 &#x1f3c5; &#x1f516;行路有良友&#xff0c;便是天堂&#x1f516; 在上一章节中&#xff0c;我们详细探讨了Pod的概念、创建、…

逐步学习Go-协程goroutine

参考&#xff1a;逐步学习Go-协程goroutine – FOF编程网 什么是线程&#xff1f; 简单来说线程就是现代操作系统使用CPU的基本单元。线程基本包括了线程ID&#xff0c;程序计数器&#xff0c;寄存器和线程栈。线程共享进程的代码区&#xff0c;数据区和操作系统的资源。 线…

数据结构——排序算法

1、排序的概念 排序是指的是将一组数据&#xff08;如数字、单词、记录等&#xff09;按照某种特定的顺序&#xff08;升序或降序&#xff09;进行排列的过程。排序算法是实现排序的程序或方法&#xff0c;它们在软件开发和数据处理中扮演着至关重要的角色。 排序算法可以根据…

servlet开发详解

一、什么是servlet&#xff0c;干什么用的&#xff1f;&#xff1f;&#xff1f; tomcat作为一个web服务器&#xff0c;也称作servlet容器。servlet只有放在web服务器中才能运行&#xff0c;不能独立运行。tomcat这个容器要做三件事&#xff1a;接收请求、处理请求和响应请求。…

文生视频大模型Sora的复现经验

大家好&#xff0c;我是herosunly。985院校硕士毕业&#xff0c;现担任算法研究员一职&#xff0c;热衷于机器学习算法研究与应用。曾获得阿里云天池比赛第一名&#xff0c;CCF比赛第二名&#xff0c;科大讯飞比赛第三名。拥有多项发明专利。对机器学习和深度学习拥有自己独到的…

java调用jacob进行文件转换ppt转pdf或者png

java调用jacob进行文件转换ppt转pdf或者png 前情提要 最近项目上&#xff0c;遇到一个复杂的ppt&#xff0c;最终要求是要将ppt每一页转成图片原本这个是不难&#xff0c;网上一搜一大堆案例&#xff0c;外加我本身也比较精通aspose&#xff0c;那还不是分分钟搞定。结果就是…