DS八大排序之归并排序和计数排序

news2024/11/19 13:43:47

前言

前几期我们详细介绍了插入排序(直接插入排序和希尔排序)、选择排序(直接选择和堆排序)、交换排序(冒泡排序和快速排序)。并对快排的各个版本做了详细的介绍,本期我们来介绍把最后两个即外部排序:归并排序 和 非比较:计数排序。

本期内容介绍

归并排序递归版

归并排序非递归版

计数排序

归并排序

归并排序递归版

基本思路:将两个有序的子序列合并成一个有序的序列的过程~!

具体过程:将一个无序的序列分成两个长度相等或相差1 的两个左右子序列分别对左右的两个子序列重复上述操作,直到只有一个元素,开始往回归并即取较小的尾插~!一组归并完了拷贝回原数组,再去归并另一组,直至整个序列有序~!由于数组不像链表可以直接拿下来,所以得借助一个第三方的辅助空间~!

OK,干说理论可能不太清楚我来画个图理解一下:

这就是一个归并的过程,但注意的是,他的分割不是又生成新的小数组,而是通过下标控制的!下面的归并也是,不是每次归并就开一个对应大小的数组而是一开始就开一个和原数组一样大的,通过下标的控制即可~!有没觉得这个和前面二叉树的那个后序遍历相似!

所以上面的图实际上是下面这样:

OK,上代码

void MergeSort(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);//辅助空间
	if (NULL == tmp)
	{
		perror("malloc failed");
		exit(-1);
	}

	_MergeSort(a, tmp, 0, n - 1);
	free(tmp);
}

这里为了不每次归并都开空间,我们一开始先开好,然后以以参数的形式传过去~!

void _MergeSort(int* a, int* tmp, int begin, int end)
{
	if (begin >= end)//只有一个或越界时不要再分了
		return;

	int mid = begin + (end - begin) / 2;//找中间的位置
	_MergeSort(a, tmp, begin, mid);//分割左区间
	_MergeSort(a, tmp, mid + 1, end);//分割右区间

	int begin1 = begin, end1 = mid;//左区间的开始和结束
	int begin2 = mid + 1, end2 = end;//右区间的开始和结束
	int index = begin;//开始归并的辅助空间的起始位置就是当前区间的开始位置
	while (begin1 <= end1 && begin2 <= end2)//归并
	{
		if (a[begin1] < a[begin2])
		{
			tmp[index++] = a[begin1++];
		}
		else
		{
			tmp[index++] = a[begin2++];
		}
	}
       
    //把剩余的放在后面
	while (begin1 <= end1)
	{
		tmp[index++] = a[begin1++];
	}

	while (begin2 <= end2)
	{
		tmp[index++] = a[begin2++];
	}

	//拷贝回去
	for (int i = begin; i <= end; i++)
	{
		a[i] = tmp[i];
	}

	//memcpy(a + begin, tmp + begin, (end - begin + 1) * sizeof(int));
}

OK,测试一下:

OK,没有问题~!递归版本的是比较简单的,我们下面来实现一下非递归版本的~!

归并排序非递归版

和递归的思路反着来,从下往上一个一个一组归并,一一组归并好了,两两一组往上归并...,直到整个序列有序~!

OK,还是画个图:

这里的问题就是,如何控制每次归并的子序列的范围?以及什么时候结束归并?

一、gap 控制几个为一组归并(gap一开始从1开始),则:

第一个子序列的起始是begin1 = i, end1 = i + gap -1;

第二个子序列的起始是begin2 = i+gap, end2 = i + 2 *gap - 1;

其中i是遍历一遍待排序的数组的下标,i从0开始。但注意的是,i每次跳几步呢?如下图,i每次应该跳2*gap步。

二、gap控制的是每次几个为一组我们 一开始是1个,2个、4个、8个,显然是2的倍数,所以gap每次乘等2即可!也不能一直让gap*=2下去,gap不可能大于等于数组的长度,所以当超过数组的长度是结束!

还有一个比较坑爹的点就是,数组的长度不一定给是偶数啊,上面介绍的只是因于数组长度是偶数的情况,非偶数的情况gap*=2,后面如果一分为2 的长度不够,就会越界,如下图。

解决方案:判断,当begin2\end2\end1越界时判断一下,当end2越界时,说明前面的区间都存在,只需要end2调整到n-1的位置即可。当begin2\end1越界时说明后面的区间不存在,直接不要排序了,又因为end1越界时begin2必定越界,所以可以直接用begin2来判断也可以~!

OK, 介绍到这里就可以上代码了:

void MergeSortNoR(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL)
	{
		perror("malloc failed");
		exit(-1);
	}

	int gap = 1;
	while (gap < n)
	{
		for (int i = 0; i < n; i += 2*gap)
		{
			int begin1 = i, end1 = i + gap - 1;
			int begin2 = i + gap, end2 = i + 2 * gap - 1;
			//第二组不存在就不要排了
			if (begin2 >= n)//if (end1 >= n ||begin2 >= n)
			{
				break;
			}
			//当end2越界时调整到n-1的位置
			if (end2 >= n)
			{
				end2 = n - 1;
			}

			int index = i;
			while (begin1 <= end1 && begin2 <= end2)
			{
				if (a[begin1] < a[begin2])
				{
					tmp[index++] = a[begin1++];
				}
				else
				{
					tmp[index++] = a[begin2++];
				}
			}

			while (begin1 <= end1)
			{
				tmp[index++] = a[begin1++];
			}

			while (begin2 <= end2)
			{
				tmp[index++] = a[begin2++];
			}

			//拷贝回去
			//for (int k = i; k <= end2; k++)
			//{
			//	a[k] = tmp[k];
			//}

			memcpy(a + i, tmp + i, (end2 - i + 1) * sizeof(int));
		}

		gap *= 2;
	}

	free(tmp);
}

测试一下:

复杂度分析

时间复杂度:O(N*logN)

我们上面说过,他像二叉树的后序遍历,高度是logN,每一层合计归并时O(N)遍历一遍数组

空间复杂度:O(N)

N为辅助数组的长度,和原数组的长度一样!

计数排序

计数排序是一种非比较排序,他又称鸽巢原理或抽屉原理(小学数学)。其实如果你学过哈希表的话,你就知道它实际上是哈希寻址的一种变形而已!(哈希全家桶的那一套会在后面C++介绍和实现)

思路:统计每个元素的出现的次数,根据统计的结果把元素放到原数组中!

什么意思?白话一点说就是,一个元素(也有可能是处理过的元素)作为统计数组的下标,每出现一次都会记录一次。等统计结束后根据每个位置出现的次数依次放回原数组放的元素就是统计数组的下标(或下标+处理的值)~!

注意这里统计的话要有一个专门的统计数组,数组的大小为最大值-最小值+1(保证所有的数都可以被统计)。

OK,画个图:

这里还有一个问题就是如果,要排序的数组元素是,100,102,101,111,110,199,188,200

按上述的话,开200个空间,前半部分浪费了。如何将解决呢?其实我们只需要映射一下就OK了,每个元素统计时减去最小值,放回是 每个元素再加上即可~!也就是100是最小的,100-100==0,即100在0号下标的位置++一下即可~!这样旧只需要开max-min +1个空间了,节省了不必要的空间~!

这就是上面说的处理过的元素~!

上代码:

//计数排序
void CountSort(int* a, int n)
{
    //找最大和最小的元素
	int min = a[0], max = a[0];
	for (int i = 0; i < n; i++)
	{
		if (min > a[i])
			min = a[i];

		if (max < a[i])
			max = a[i];
	}
    
    //开max - min + 1 的空间
	int* tmp = (int*)calloc((max - min + 1), sizeof(int));
	if (NULL == tmp)
	{
		perror("malloc failed");
		exit(-1);
	}
    
    //统计出现的次数
	for (int i = 0; i < n; i++)
	{
		tmp[a[i] - min]++;
	}
    
    //依次给原数组即可
	int index = 0;
	for (int i = 0; i < max - min + 1; i++)
	{
		while (tmp[i]--)
		{
			a[index++] = i + min;
		}
	}
}

OK,测试一下:

复杂度分析

时间复杂度:O(N+K)

N为原数组的大小, K为统计数组的大小。遍历一遍数组找最大和最小O(N),统计O(N), 拷贝回去O(N+K)遍历一遍统计数组的同时还要遍历一原数组!

空间复杂度:O(K)

K是统计数组的大小~!

注意:计数排序效率还可以,但他的致命缺陷就是比较适合数据相对集中的数字据,如果不集中的话或很浪费空间例如:1,2,5,3,4,0,888,9999,1111,11,7,4这样的数据。

OK~!本期本想就到这里,好兄弟我们下期再见~!

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

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

相关文章

关于“Python”的核心知识点整理大全41

目录 scoreboard.py game_functions.py game_functions.py 14.3.8 显示等级 game_stats.py scoreboard.py scoreboard.py scoreboard.py game_functions.py game_functions.py alien_invasion.py 14.3.9 显示余下的飞船数 ship.py scoreboard.py 我们将最高得分圆整…

大数据与人工智能|全面数字化战略与企业数字化转型(第1节 )

要点一&#xff1a;培养跨学科思维 在分析时&#xff0c;需要采用多学科的思维方式 结果不重要&#xff0c;重要的是如何提炼现象、分析问题和得出结论的过程。 1. 介绍了锤子精神和多学科思维方式的重要性。指出了只从自身学科出发解决问题的局限性。 2. 提倡跨学科思维方式&a…

家校互通小程序实战开发02首页搭建

目录 1 创建应用2 搭建首页总结 我们上一篇介绍了家校互通小程序的需求&#xff0c;创建了对应的数据源。有了这个基础的分析之后&#xff0c;我们就可以进入到开发阶段了。开发小程序&#xff0c;先需要创建应用。 1 创建应用 登录控制台&#xff0c;点击创建应用&#xff0c…

2024年深度学习、计算机视觉与大模型面试题综述,六大专题数百道题目

DeepLearning-Interview-Awesome-2024 本项目涵盖了大模型(LLMs)专题、计算机视觉与感知算法专题、深度学习基础与框架专题、自动驾驶、智慧医疗等行业垂域专题、手撕项目代码专题、优异开源资源推荐专题共计6大专题模块。我们将持续整理汇总最新的面试题并详细解析这些题目&a…

元宇宙与VR虚拟现实的未来如何?

从科幻小说到商业现实 自从 Facebook年更名为 Meta 以来&#xff0c;关于元宇宙的热议不断&#xff0c;人们对虚拟世界的兴趣也重新燃起&#xff0c;因为尽管虚拟现实 (VR) 的概念由来已久&#xff0c;但该技术现在才开始真正得以应用。 定义元宇宙和虚拟现实 首先是 The Met…

玩客云 青龙面板

一、刷机 需要的工具&#xff0c;镊子&#xff0c;双公头USB&#xff08;可以自己做&#xff09;&#xff0c;U盘 青龙面板全教程 | Anubis的小窝 powersee教程 玩客云导航固件使用说明 安装教程 玩客云乱七八糟的坑 静态IP配置 玩客云第二版固件说明 docker 下载器 …

摇杆控制人物移动

摇杆控制人物移动 一、UI搭建二、3d模型搭建三、脚本JoyStickBar.csPlayerController.cs 工程在我资源里名字叫Joystickbar.unitypackage [连接](https://download.csdn.net/download/qq_42194657/12043019?spm1001.2014.3001.5503) 一、UI搭建 JoyStickBar是图片背景 JoySt…

【MATLAB第86期】基于matlab的Catboost多输入单输出分类预测模型 catboost-1.1.1版本

基于matlab的Catboost多输入单输出分类预测模型 catboost-1.1.1版本 运行环境 windows10 matlab2020a catboost版本&#xff1a;catboost-1.1.1 往期&#xff1a; 【MATLAB第20期】基于matlab的Catboost多输入单输出回归预测模型 catboost-1.1.1版本 一、导入数据 采用12输…

sqlilabs第三十六三十七关

Less-36&#xff08;GET - Bypass MySQL_real_escape_string) 手工注入 单引号闭合&#xff08;单引号自动转换的编码变了直接输入%27&#xff09; 自动注入 和上一关一样 Less-37&#xff08;POST - Bypass MySQL_real_escape_string) 手工注入 这个也是碰到过的情况 接下…

WizFi360-EVB-Pico评估版介绍

文章目录 1 概述2 硬件资源2.1 硬件规格2.2 引脚定义2.3 工作条件 3 参考资料3.1 Datasheet3.2 原理图3.3 尺寸图(单位 : mm) 3.4 参考例程 1 概述 WizFi360-EVB-Pico基于树莓派RP2040&#xff0c;并使用WizFi360增加Wi-Fi连接。它与树莓派Pico板引脚兼容&#xff0c;可用于物联…

城市自贸区/经开区/产业园基于EasyCVR视频技术的可视化、移动化、智能化视频监管方案

一、背景需求 移动互联网的发展激发了用户对轻应用的使用习惯。4G、5G使得无线带宽快速提升&#xff0c;令大流量视频数据流逐渐从PC往手持终端转移。借助智能手持终端也可以实时查看、远程控制、存储录像、抓拍图像&#xff0c;能方便快捷地掌握所关注区域的视频动态。 随着…

prometheus api调用案例(代码+curl)

只有干货哦&#xff01; 目录 支持的api列表 代码调用 库 k8s集群下的prom 非容器部署的prom调用 curl调用 示例1&#xff1a;查询数据 示例2&#xff1a;热加载配置 示例3&#xff1a;主动删除数据 支持的api列表 源码位置&#xff1a;github.com\prometheus\client_…

鸿蒙操作系统:从手机到物联网,打造全场景智能体验

随着科技的不断发展&#xff0c;人们对于操作系统的需求也在不断升级。鸿蒙操作系统&#xff0c;作为华为推出的新一代智能终端操作系统&#xff0c;凭借其强大的分布式能力、流畅的用户体验以及丰富的应用生态&#xff0c;正逐渐成为人们关注的焦点。 一、鸿蒙操作系统概述 …

【论文笔记】BiFormer: Vision Transformer with Bi-Level Routing Attention

论文地址&#xff1a;BiFormer: Vision Transformer with Bi-Level Routing Attention 代码地址&#xff1a;https://github.com/rayleizhu/BiFormer vision transformer中Attention是极其重要的模块&#xff0c;但是它有着非常大的缺点&#xff1a;计算量太大。 BiFormer提…

AI数字人直播:引领直播行业新风潮,塑造新一轮“风口”

直播行业自从出现以来&#xff0c;就一直在不断地迭代和创新。而如今&#xff0c;随着AI技术的迅猛发展&#xff0c;AI数字人直播成为了一个崭新的赛道&#xff0c;引领着直播行业的新风潮&#xff0c;并有望塑造出新一轮的“风口”。 AI数字人直播&#xff0c;顾名思义&#…

LENOVO联想笔记本小新Pro 14 IRH8 2023款(83AL)电脑原装出厂Win11系统恢复预装OEM系统

链接&#xff1a;https://pan.baidu.com/s/1M1iSFahokiIHF3CppNpL4w?pwdzr8y 提取码&#xff1a;zr8y 联想原厂系统自带所有驱动、出厂主题壁纸、Office办公软件、联想电脑管家等自带的预装软件程序 所需要工具&#xff1a;16G或以上的U盘 文件格式&#xff1a;ISO 文件…

python3遇到Can‘t connect to HTTPS URL because the SSL module is not available.

远程服务器centos7系统上有minicoda3&#xff0c;觉得太占空间&#xff0c;就把整个文件夹删了&#xff0c;原先的Python3也没了&#xff0c;都要重装。 我自己的步骤&#xff1a;进入管理员模式 1.下载Python3的源码&#xff1a; wget https://www.python.org/ftp/python/3.1…

平面灯阵中寻找最大正方形边界 - 华为机试真题题解

分值: 300分 题解: Java / Python / C++ 题目描述 现在有一个二维数组来模拟一个平面灯阵,平面灯阵中每个位置灯处于点亮或熄灭,分别对应数组每个元素取值只能为1或0,现在需要找一个正方形边界,其每条边上的灯都是点亮(对应数组中元素的值为1)的,且该正方形面积最大。 …

Linux第一个小程序-进度条(c语言版)

目录 行缓冲区概念&#xff1a; 行缓冲区代码演示&#xff1a; ​编辑进度条代码 1&#xff1a;memset函数&#xff1a; 2&#xff1a;const char* lable"|/-\\"; 3&#xff1a;usleep C语言 usleep 函数的功能和用法&#xff1a; 4&#xff1a;进度条代码的实…

设置视图的对齐方式

对齐方式 在XML文件中通过属性android:layout_gravity可以指定当前视图的对齐方向&#xff0c;当属性值为top时表示视图朝上对齐&#xff0c;为bottom时表示视图朝下对齐&#xff0c;为left时表示视图靠左对齐&#xff0c;为right时表示视图靠右对齐。如果希望视图既朝上又靠左…