热门标签 | HotTags
当前位置:  开发笔记 > 编程语言 > 正文

算法题,c++,分治算法,合并K个升序链表,原链表修改链接顺序降低空间复杂度,分治合并降低时间复杂度

算法题,c++,分治算法,合并K个升序链表,原链表修改链接顺序降低空间复杂度,分治合并降低时间复杂度题目:leetcode给你一个链表数组,每个链表都已经按升序排列。请你将所有链表

算法题,c++,分治算法,合并K个升序链表,原链表修改链接顺序降低空间复杂度,分治合并降低时间复杂度

题目:leetcode
给你一个链表数组,每个链表都已经按升序排列。请你将所有链表合并到一个升序链表中,返回合并后的链表。

链表结点结构:

typedef 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) { }
}ListNode;

思路:
1、K个链表数据量很大,不适合新建容器或新建链表然后排序,可以利用链表本身的next属性,改变每个结点的后接结点,以达到节省空间的效果。
2、K个链表同时对首结点的数据进行比较不方便,如果是两个链表就可以很方便的使用if进行判断,因此在K个链表中两两相合,两两相合的结果再次两两相合,合到只有一个链表,就是最终的结果了。

代码与每一行的注释,一定注意看注释,看注释,不然有可能看不懂代码的:

//合并两个链表为一个链表
ListNode* AddToOne(ListNode* AList, ListNode* BList)
{
//判断是否传入的两个链表中有空的链表
if (!AList || !BList)
{
return (AList ? AList : BList);
}
//用于组合新链表的链表头
ListNode* Tail;
//链表头不能直接使用,新建一个临时结点提供给链表头
//这个空链表头还用于找到链表的头,避免在合并完以后无法获取到链表的起点
//临时结点还用于返回链表头,通过return Head.next
ListNode Head;
Tail = &Head;
//用于遍历两个链表的所有内容和比较链表结点数据的指针
ListNode* pA = AList;
ListNode* pB = BList;
//当两个遍历的结点A,B都有值时,说明两个链表中的数据还没有遍历完,
//只要有一个遍历完了,就表示两个合并为一个了,且无法继续进行比较了
//因此while循环的判断条件是两个结点都有值而不是空
while (pA && pB)
{
//哪个小就把哪个放到新链表的尾部
if (pA->val < pB->val)
{
Tail->next = pA;
pA = pA->next;
}
else
{
Tail->next = pB;
pB = pB->next;
}
//装入尾部后将新链表的当前结点向后移,保持在最后位
Tail = Tail->next;
}
//判断结束后,一个链表变成空了,剩下还有一部分链表需要另外加入到新链表
Tail->next = (pA ? pA : pB);
return Head.next;
}
//使用分治算法将链表vector进行分治合并
//每一次合并只能合并两个下标的链表,将两个链表的所在下标表示为left,right
ListNode* merge(vector<ListNode*>& Data,int left,int right)
{
//当左下标和右下标相等时,返回该下标对应的链表就可以
if (left == right)
{
return Data[left];
}
//当左下标和右下标紧跟着时,就可以合并了,然后将合并的结果返回
else if(left + 1 == right)
{
return AddToOne(Data[left], Data[right]);
}
//当左下标和右下标没有相等,没有紧跟着时,递归下去
//向下递归的规律为:从最左边到中间的合并,中间后一个结点到最右边的合并,得到的两个链表合并后将链表返回
else
{
return AddToOne(merge(Data, left, (left + right) / 2 ), merge(Data, (left + right) / 2 + 1, right));
}
}
ListNode* mergeKLists(vector<ListNode*>& Data)
{
//传入的vector有可能是个空的,为了避免后面的size-1为负数,有必要对size的值进行判断
if (Data.size() == 0)
{
return NULL;
}
return merge(Data, 0, Data.size() - 1);
}

推荐阅读
  • 本文介绍了H5游戏性能优化和调试技巧,包括从问题表象出发进行优化、排除外部问题导致的卡顿、帧率设定、减少drawcall的方法、UI优化和图集渲染等八个理念。对于游戏程序员来说,解决游戏性能问题是一个关键的任务,本文提供了一些有用的参考价值。摘要长度为183字。 ... [详细]
  • 云原生边缘计算之KubeEdge简介及功能特点
    本文介绍了云原生边缘计算中的KubeEdge系统,该系统是一个开源系统,用于将容器化应用程序编排功能扩展到Edge的主机。它基于Kubernetes构建,并为网络应用程序提供基础架构支持。同时,KubeEdge具有离线模式、基于Kubernetes的节点、群集、应用程序和设备管理、资源优化等特点。此外,KubeEdge还支持跨平台工作,在私有、公共和混合云中都可以运行。同时,KubeEdge还提供数据管理和数据分析管道引擎的支持。最后,本文还介绍了KubeEdge系统生成证书的方法。 ... [详细]
  • Java容器中的compareto方法排序原理解析
    本文从源码解析Java容器中的compareto方法的排序原理,讲解了在使用数组存储数据时的限制以及存储效率的问题。同时提到了Redis的五大数据结构和list、set等知识点,回忆了作者大学时代的Java学习经历。文章以作者做的思维导图作为目录,展示了整个讲解过程。 ... [详细]
  • 本文介绍了Java的集合及其实现类,包括数据结构、抽象类和具体实现类的关系,详细介绍了List接口及其实现类ArrayList的基本操作和特点。文章通过提供相关参考文档和链接,帮助读者更好地理解和使用Java的集合类。 ... [详细]
  • Go语言实现堆排序的详细教程
    本文主要介绍了Go语言实现堆排序的详细教程,包括大根堆的定义和完全二叉树的概念。通过图解和算法描述,详细介绍了堆排序的实现过程。堆排序是一种效率很高的排序算法,时间复杂度为O(nlgn)。阅读本文大约需要15分钟。 ... [详细]
  • 集合的遍历方式及其局限性
    本文介绍了Java中集合的遍历方式,重点介绍了for-each语句的用法和优势。同时指出了for-each语句无法引用数组或集合的索引的局限性。通过示例代码展示了for-each语句的使用方法,并提供了改写为for语句版本的方法。 ... [详细]
  • JDK源码学习之HashTable(附带面试题)的学习笔记
    本文介绍了JDK源码学习之HashTable(附带面试题)的学习笔记,包括HashTable的定义、数据类型、与HashMap的关系和区别。文章提供了干货,并附带了其他相关主题的学习笔记。 ... [详细]
  • 十大经典排序算法动图演示+Python实现
    本文介绍了十大经典排序算法的原理、演示和Python实现。排序算法分为内部排序和外部排序,常见的内部排序算法有插入排序、希尔排序、选择排序、冒泡排序、归并排序、快速排序、堆排序、基数排序等。文章还解释了时间复杂度和稳定性的概念,并提供了相关的名词解释。 ... [详细]
  • 本文介绍了Redis的基础数据结构string的应用场景,并以面试的形式进行问答讲解,帮助读者更好地理解和应用Redis。同时,描述了一位面试者的心理状态和面试官的行为。 ... [详细]
  • 本文介绍了C#中数据集DataSet对象的使用及相关方法详解,包括DataSet对象的概述、与数据关系对象的互联、Rows集合和Columns集合的组成,以及DataSet对象常用的方法之一——Merge方法的使用。通过本文的阅读,读者可以了解到DataSet对象在C#中的重要性和使用方法。 ... [详细]
  • eclipse学习(第三章:ssh中的Hibernate)——11.Hibernate的缓存(2级缓存,get和load)
    本文介绍了eclipse学习中的第三章内容,主要讲解了ssh中的Hibernate的缓存,包括2级缓存和get方法、load方法的区别。文章还涉及了项目实践和相关知识点的讲解。 ... [详细]
  • 本文详细介绍了Linux中进程控制块PCBtask_struct结构体的结构和作用,包括进程状态、进程号、待处理信号、进程地址空间、调度标志、锁深度、基本时间片、调度策略以及内存管理信息等方面的内容。阅读本文可以更加深入地了解Linux进程管理的原理和机制。 ... [详细]
  • 本文探讨了C语言中指针的应用与价值,指针在C语言中具有灵活性和可变性,通过指针可以操作系统内存和控制外部I/O端口。文章介绍了指针变量和指针的指向变量的含义和用法,以及判断变量数据类型和指向变量或成员变量的类型的方法。还讨论了指针访问数组元素和下标法数组元素的等价关系,以及指针作为函数参数可以改变主调函数变量的值的特点。此外,文章还提到了指针在动态存储分配、链表创建和相关操作中的应用,以及类成员指针与外部变量的区分方法。通过本文的阐述,读者可以更好地理解和应用C语言中的指针。 ... [详细]
  • 本文介绍了Java的公式汇总及相关知识,包括定义变量的语法格式、类型转换公式、三元表达式、定义新的实例的格式、引用类型的方法以及数组静态初始化等内容。希望对读者有一定的参考价值。 ... [详细]
  • C++语言入门:数组的基本知识和应用领域
    本文介绍了C++语言的基本知识和应用领域,包括C++语言与Python语言的区别、C++语言的结构化特点、关键字和控制语句的使用、运算符的种类和表达式的灵活性、各种数据类型的运算以及指针概念的引入。同时,还探讨了C++语言在代码效率方面的优势和与汇编语言的比较。对于想要学习C++语言的初学者来说,本文提供了一个简洁而全面的入门指南。 ... [详细]
author-avatar
gaoyizhen92
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有