合并多个有序数组

news2025/1/12 12:19:21

合并多个有序数组

  • 题目描述
  • 思想
  • 代码实现
  • 变形题目

题目描述

我们现在有多个已经有序的数组,我们知道每个有序数组的元素个数和总共的有序数组的个数,现在请设计一个算法来实现这多个有序数组的合并(合并成一个数组);
例如:
输入:[1,2,3,4,5,],[6,7],[8,9,10],[11,12,13,14];
输出:[1,2,3,4,5,6,7,8,9,10,11,12,13,14];

函数接口:

int* MulArrSort(int** nums, int Row, int* Col, int* returnSize)
{
  //returnSize//返回合并的数组的元素个数
  //Row有多少个有序数组
  //Col数组,表示每个有序数组里面有多少个元素
}

思想

首先这道题的思路与合并两个有序数组 这道题的思路非常相似,都是每次从有序数组中选出较小的值出来,然后较小的值的下标往后挪动;
在这里插入图片描述

本题的思想也是如此,我们可以先将每个有序数组的最小值圈在一起,然后从这些最小值中选出较小的放入临时数组中,并且该较小值的下标也往后挪动一下:
比如:
在这里插入图片描述
在这里插入图片描述
现在问题的关键是,我们怎么从这些数据中选出较小值,解决了这个问题本题也就轻松一半!
有两个方法
1、暴力选举
就对这些数一个一个遍历,选出最小值,时间复杂度O(K),时间开销太大了;
2、建立小堆
我们可以对这些数据进行建立小堆处理,然后堆顶元素就是这些数据中的较小值!时间复杂度也很乐观:O(logK);
为此我们选择第二种办法:
但是我们是直接对这些单一数据进行建堆吗?
当然不是,首先我们得想到,我们选出了较小值然后呢?如何往后走?是在那个有序数组里面往后走呢?
这些都是我们需要思考的问题,因此为了解决如何往后走的问题,我们需要一个结构体,这个结构体:包含当前元素的下标当前数组的总元素个数当前有序数组的数组名
在这里插入图片描述

1、这就是我们建立的单个堆元素,我们以当前元素的大小来进行比较建立小堆;
2、然后每次选出堆顶元素所表示的值存储起来,同时更新堆顶元素,并重新利用向下调整算法调整小堆;
3、当某一个有序数组的元素被选完时,他就已经没有下个元素了,这时我们就需要将堆尾元素与堆顶元素进行替换,然后小堆的有效元素个数减1,而对于正常情况的话则对对顶元素进行正常替换即可;当小堆里面的有效元素没有了,就说明多个有序数组的合并也就完成了;

代码实现

struct Node
{
	int index;//记录当前元素的位置
	int* arr;//当前元素所在的数组
	int len;//当前元素所在的数组的总长度
};
void Swap(struct Node* a, struct Node* b)
{
	struct Node tmp = *a;
	*a = *b;
	*b = tmp;
}
void AdjustDown(struct Node* Heap, int top, int end)
{
	int parent = top;
	int child = 2*parent+1;
	while (child < end)
	{
		if (child + 1 < end && Heap[child + 1].arr[Heap[child + 1].index] < Heap[child].arr[Heap[child].index])
		{
			child++;
		}
		if (Heap[child].arr[Heap[child].index] >= Heap[parent].arr[Heap[parent].index])
			break;
		else
		{
			Swap(Heap+child,Heap+parent);
			parent = child;
			child = 2 * parent + 1;
		}
	}
}
int* MulArrSort(int** nums, int Row, int* Col, int* returnSize)
{
	*returnSize = 0;
	//计算合并数组的总元素个数
	for (int i = 0; i < Row; i++)
	{
		*returnSize += Col[i];
	}
	//合并的总数组,用于存储合并的元素
	int* ret = (int*)malloc(sizeof(int) * (*returnSize));
	if (!ret)
		exit(EXIT_FAILURE);
	//1、建立初始堆数组
	struct Node* Heap = (struct Node*)malloc(sizeof(struct Node) * (Row));
	if (!Heap)
		exit(EXIT_FAILURE);
	int size = 0;//记录有效数组个数
	for (int i = 0; i < Row; i++)
	{
		if (nums[i])//如果给的有序数组中有空数组,我们就不将其考虑进去建堆中
		{
			Heap[size].arr = nums[i];
			Heap[size].index = 0;
			Heap[size].len = Col[i];
			size++;
		}
	}
	int HeapSize = size;
	//2、建立小堆
	for (int top = (HeapSize - 1 - 1) / 2; top >= 0; top--)
	{
		AdjustDown(Heap,top,HeapSize);
	}
	int k = 0;//记录合并数组的下标
	struct Node NextNode ={ 0 };
	//3、开始选数
	while (HeapSize)
	{
		ret[k++] = Heap[0].arr[Heap[0].index];//存入堆顶元素
		NextNode = Heap[0];
		NextNode.index++;//下标往后挪
		//4、更新小堆
		if (NextNode.index >= NextNode.len)//说明某一组有序数组已经选完了,这时候堆顶元素的跟新不需要特俗处理,由于当前有序数组已经遍历完毕,没有下一个元素,因此NextNode是无效数据,因此堆顶数据不能用NextNode更新,需要用堆尾元素更新,然后有效小堆元素个数--;
		{
			Swap(Heap+HeapSize-1,Heap);
			HeapSize--;//有效元素--;
		}
		else//正常来到下一次的位置
		{
			Heap[0] = NextNode;//正常更新下一次位置
		}
		AdjustDown(Heap,0,HeapSize);
	}
	free(Heap);
	return ret;
}

测试:
在这里插入图片描述
时间复杂度:O(K x n x logK)
空间复杂度:O(K)

变形题目

合并有序链表;
参考代码:

struct ListNode*BuyNode(int val)
{
    struct ListNode*Node=(struct ListNode*)malloc(sizeof(struct ListNode));
    Node->val=val;
    Node->next=NULL;
    return Node;
}
void Swap(struct ListNode**a,struct ListNode**b)
{
    struct ListNode*tmp=*a;
    *a=*b;
    *b=tmp;
}
void AdjustDown(struct ListNode** lists,int top,int end)
{
  int parent=top;
  int child=2*top+1;
  while(child<end)
  {
      if(child+1<end&&lists[child+1]->val<lists[child]->val)
      child++;
      if(lists[child]->val>=lists[parent]->val)
      break;
      else
      {
        Swap(lists+parent,lists+child);
        parent=child;
        child=2*parent+1;
      }
  }
}
struct ListNode* mergeKLists(struct ListNode** lists, int listsSize){
         struct ListNode** Heap=(struct ListNode**)malloc(sizeof(struct ListNode*)*listsSize);
         int size=0;
         for(int i=0;i<listsSize;i++)
         {
             if(lists[i])
             {
                 Heap[size++]=lists[i];
             }
         }
         int HeapSize=size;
         for(int top=(HeapSize-2)/2;top>=0;top--)
         {
             AdjustDown(Heap,top,HeapSize);
         }
         struct ListNode*dummyhead=(struct ListNode*)malloc(sizeof(struct ListNode));
         dummyhead->next=NULL;
         struct ListNode*cur=dummyhead;
         while(HeapSize)
         {
             struct ListNode*next=Heap[0]->next;
             struct ListNode*NewNode=BuyNode(Heap[0]->val);
             cur->next=NewNode;
             cur=NewNode;
             if(next)
             {
                 Heap[0]=next;
             }
             else
             {
                 Swap(Heap,Heap+HeapSize-1);
                 HeapSize--;
             }
             AdjustDown(Heap,0,HeapSize);
         }
         struct ListNode*Head=dummyhead->next;
         free(dummyhead);
         free(Heap);
         return Head;
}

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

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

相关文章

Chrome浏览器可以用ChatGPT了?

程序员宝藏库&#xff1a;https://gitee.com/sharetech_lee/CS-Books-Store 最近这段时间想必 和我一样&#xff0c;都被chatGPT刷屏了。 在看到网上给出的一系列chatGPT回答问题的例子和自己亲自体验之后&#xff0c;的确发现它效果非常令人惊艳。 chatGPT的火热程度在开源社…

turbo编码原理

一、原理 Turbo的编码器由两个并行的分量编码器组成。分量编码器的选择一般是卷积码。在Turbo码中&#xff0c;输入序列在进入第二个编码器时须经过一个交织器 &#xff0c;用于将序列打乱。两个编码器的输出共同作为冗余信息添加到信息序列之后&#xff0c;对抗信道引起的错误…

实战SupersetBI报表之数据集图表配置

上集已经安装完Superset -实战SupersetBI报表之安装 本集开始讲解 根据数据集配置图表&#xff1a;以简单的员工花名册 为例 1、首先配置数据库 上次安装的时候也提到过 如果服务之间都是docker 安装。必须保证能够通信 下面根据实际参数配置即可 当我们配置好数据库之后 就可以…

如何保证TCP传输的可靠性

重传机制&#xff0c;流量控制&#xff0c;拥塞控制 1.重传机制&#xff1a; 序列号确认应答 当发送端的数据到达接收主机的时候&#xff0c;接收端主机会返回一个确认应答消息&#xff0c;表示已经收到消息 当数据发生丢包时&#xff0c;用重传机制解决 重传机制有好几种…

【Anime.js】——用Anime.js实现动画效果

目录 目标&#xff1a; ​编辑1、确定思路 2、创建网格 3、设置随机位置 4、创建时间轴动画 完整代码&#xff1a; 目标&#xff1a; 实现自动选点&#xff0c;对该点进行先缩小后放大如何回到比其他点大一点的状态&#xff0c;并以该点从外向内放大 1、确定思路 2、创建网…

第12届嵌入式蓝桥杯真题-停车场管理系统的设计与实现

目录 实验要求&#xff1a; 实验思路&#xff1a; 核心代码&#xff1a; &#xff08;1&#xff09;主函数 &#xff08;2&#xff09;lcd显示 &#xff08;3&#xff09;按键函数 &#xff08;4&#xff09;LED显示函数 &#xff08;5&#xff09;业务处理函数 &…

深度理解取模

深度理解取模一.取模概念二.负数取模三.进一步的解释四.取模和取余是一样的吗&#xff1f;一.取模概念 二.负数取模 上面的代码一目了然就不再多少啦&#xff0c;但如果是负数取模又该怎么办呢&#xff1f; 以上a/b-3是很好理解的&#xff0c;那为什么取模后的值是-1呢&#xf…

useEffect 和 useLayoutEffect 的源码解读

文章目录useEffect 和 useLayoutEffect 的源码解读useEffect源码解读mountEffectImplpushEffectupdateEffectImpl疑惑&#xff1a;useLayOutEffect源码解读mountLayoutEffectupdateLayoutEffect总结useEffect 和 useLayoutEffect 的源码解读 useEffect 文件在 packages/react…

毕业设计-基于深度学习的交通标识识别-opencv

目录 前言 课题背景和意义 实现技术思路 实现效果图样例 前言 &#x1f4c5;大四是整个大学期间最忙碌的时光,一边要忙着备考或实习为毕业后面临的就业升学做准备,一边要为毕业设计耗费大量精力。近几年各个学校要求的毕设项目越来越难,有不少课题是研究生级别难度的,对本科…

叠氮-聚乙二醇-羧基,N3-PEG-COOH 异双功能PEG衍生物COOH-PEG-N3又名Azide-PEG-acid,可用于修饰蛋白质

英文名称&#xff1a;N3-PEG-COOH&#xff1b;COOH-PEG-N3 Azide-PEG-acid 中文名称&#xff1a;叠氮-聚乙二醇-羧基&#xff1b;羧基-聚乙二醇-叠氮 存储条件&#xff1a;-20C&#xff0c;避光干燥 用 途&#xff1a;仅用于科学研究&#xff0c;不能用于人体 外观: 固体或…

【OpenFeign】【源码+图解】【一】HelloWorld及其工作原理

目录1. HelloWorld1.1 pom.xml1.2 yml配置1.3 开启OpenFeign1.4 创建Product的FeignClient1.5 Controller2. OpenFeign的工作原理1. HelloWorld 服务中心以及Product微服务继续使用LoadBalance中的HelloWorld&#xff0c;新建一个client作为OpenFeign的例子 1.1 pom.xml 引入…

【视觉高级篇】26 # 如何绘制带宽度的曲线?

说明 【跟月影学可视化】学习笔记。 如何用 Canvas2D 绘制带宽度的曲线&#xff1f; Canvas2D 提供了相应的 API&#xff0c;能够绘制出不同宽度、具有特定连线方式&#xff08;lineJoin&#xff09;和线帽形状&#xff08;lineCap&#xff09;的曲线&#xff0c;绘制曲线非…

力扣(1053.115)补9.13

1053.不相交的线 不会&#xff0c;这题和1143题代码一样&#xff0c;只不过题意不太能懂&#xff0c;难想&#xff0c;难想。 115.不同的子序列 难想&#xff0c;这种题就跟该画dp图去理解&#xff0c;太难了。 跟着图去模拟一下&#xff0c;要不然真的答案都看不懂。 dp[i][j…

Servlet知识

1、什么是Servlet&#xff1f; Servlet&#xff08;Server Applet&#xff09;是Java Servlet的简称&#xff0c;称为小服务程序或服务连接器&#xff0c;用Java编写的服务器端程序&#xff0c;具有独立于平台和协议的特性&#xff0c;主要功能在于交互式地浏览和生成数据&…

机器学习笔记:scikit-learn pipeline使用示例

0. 前言 在机器学习中&#xff0c;管道机制是指将一系列处理步骤串连起来自动地一个接一个地运行的机制。Scikit-Learn提供了pipeline类用于实现机器学习管道&#xff0c;使用起来十分方便。 既然要将不同处理步骤串联起来&#xff0c;首先必须确保每个步骤的输出与下一个步骤的…

java计算机毕业设计基于安卓Android的数字猎头招聘管理APP

项目介绍 网络的广泛应用给生活带来了十分的便利。所以把数字猎头招聘管理与现在网络相结合,利用java技术建设数字猎头招聘管理APP,实现数字猎头招聘管理的信息化。则对于进一步提高数字猎头招聘管理发展,丰富数字猎头招聘管理经验能起到不少的促进作用。 数字猎头招聘管理APP能…

kafka概念及部署

文章目录一.kafka1.kafka的概念2.Kafka的特性3.工作原理4.文件存储5.消息模式5.1点到点5.2订阅模式6.基础架构一.kafka 1.kafka的概念 Kafka是最初由Linkedin公司开发&#xff0c;是一个分布式、支持分区的&#xff08;partition&#xff09;、多副本的&#xff08;replica&a…

第八章会话控制

文章目录为什么需要会话控制带来的问题如何解决无状态的问题——Cookie如果只靠单纯的Cookie存在的问题单纯Cookie导致问题的解决方法——SessionSessionsession的结构一些关于Session的APISession的保存作用域Cookie时效性会话和持久化Cookie对比Cookie的domain和path为什么需…

后端开发框架的具体内容是什么?

在数据化管理越来越规范的今天&#xff0c;低代码开发平台也迎来了重要的发展期。前后端分离已经成为发展趋势&#xff0c;有不少客户朋友想要咨询后端开发框架的定义和内容&#xff0c;为了帮助大家答疑解惑&#xff0c;小编经过整理&#xff0c;组织出了一篇关于该内容的文章…

centos7 安装部署sonarqube 8.9.1(postqresql数据库版)

公司产品sonarqube以最大限度地提高质量并管理软件产品组合中的风险。为开发者软件开发人员最终负责代码质量。 代码质量是所谓的非功能性需求的一部分&#xff0c;因此是开发人员的直接责任。为有追求的程序员写出地道代码提供方向。 一、环境要求 1、centos7 x64 2、jdk11 3…