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

前言

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

本期内容介绍

归并排序递归版

归并排序非递归版

计数排序

归并排序

归并排序递归版

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

具体过程:将一个无序的序列分成两个长度相等或相差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~!本期本想就到这里,好兄弟我们下期再见~!

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

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

相关文章

k8s面试之——简述网络模型

kubernetes网络模型是kubernetes集群中管理容器网络通信的一种机制&#xff0c;用于实现pod间、pod与外部网络间的通信和互联&#xff0c;并提供了多种网络插件和配置选项来满足不同应用场景下的需求。kubernetes网络模型可以分为一下几个部分&#xff1a; 1. pod网络模型 在…

经典文献阅读之--OccNeRF(基于神经辐射场的自监督多相机占用预测)

0. 简介 作为基于视觉感知的基本任务&#xff0c;3D占据预测重建了周围环境的3D结构。它为自动驾驶规划和导航提供了详细信息。然而&#xff0c;大多数现有方法严重依赖于激光雷达点云来生成占据地面真实性&#xff0c;而这在基于视觉的系统中是不可用的。之前我们介绍了《经典…

cephfs cap机制介绍

一、Cap&#xff1a;概述 cap是文件系统层面的&#xff0c;包括元数据、数据操作。cap 和mds分布式锁是对应的cap是MDS分配给client对inode的操作能力权限。不同的客户端&#xff0c;或者同一客户端不同时刻&#xff0c;对同一inode持有cap可能是不同的•作用&#xff1a;MDS通…

学习笔记13——Spring整合Mybatis、junit、AOP、事务

学习笔记系列开头惯例发布一些寻亲消息 链接&#xff1a;https://baobeihuijia.com/bbhj/ Mybatis - Spring&#xff08;使用第三方包new一个对象bean&#xff09; 原始的Mybatis与数据库交互【通过sqlmapconfig来配置和连接】 初始化SqlSessionFactory获得连接获取数据层接口…

Python pandas 操作 excel 详解

文章目录 1 概述1.1 pandas 和 openpyxl 区别1.2 Series 和 DataFrame 2 常用操作2.1 创建 Excel&#xff1a;to_excel()2.2 读取 Excel&#xff1a;read_excel()2.2.1 header&#xff1a;标题的行索引2.2.2 index_col&#xff1a;索引列2.2.3 dtype&#xff1a;数据类型2.2.4 …

ansible 备忘清单(一)

笔者&#xff1a; 把以前的手写笔记电子化吧&#xff0c;顺便当作复习。 基础命令 命令 参数 备注 ansible --version 查看版本号 ansible-doc --help 查看帮助信息 -l &#xff5c;--list 查看所有模块 -s 查看模块摘要 Ansible servers -I &#xff5c;-…

天津医科大学临床医学院专升本公共事业管理专业卫生事业管理考纲

天津医科大学临床医学院高职升本科专业课考试大纲公共事业管理专业《卫生事业管理学》考试大纲 一、考试基本要求 本考试大纲为公共事业管理专业高职升本科入学考试内容&#xff0c;主要考察学生对卫生事业管理学的基本概念、基本理论以及解决问题的基本方法的掌握程度&#…

SpringBoot 2 集成Spark 3

前提条件: 运行环境&#xff1a;Hadoop 3.* Spark 3.* ,如果还未安装相关环境&#xff0c;请参考&#xff1a; Spark 初始 CentOS 7 安装Hadoop 3 单机版 SpringBoot 2 集成Spark 3 pom.xml <?xml version"1.0" encoding"UTF-8"?> <pro…

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

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

9. UVM Test

test位于启动环境组件构建的层次顶部(top of the hierarchical)。它还负责测试平台配置和激励生成过程。根据验证计划中提到的设计特征和功能&#xff0c;编写测试。用户定义的测试类源自uvm_test。 9.1 uvm_test class hierarchy 类声明&#xff1a; virtual class uvm_test …

Sublime Text 4 中文汉化教程(Version: Build 4169)

Sublime Text 4汉化 1 知识小课堂1.1 sublim简介1.2 其他编辑器 2 安装过程2.1 安装Install Package Control2.2 Install Package2.3 安装工具包2.4 常用的插件2.5 安装中文包 1 知识小课堂 1.1 sublim简介 Sublime是一款代码编辑器&#xff0c;致力于为开发人员提供快速、高…

玩客云 青龙面板

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

【MySQL】数据库中为什么使用B+树不用B树

&#x1f34e;个人博客&#xff1a;个人主页 &#x1f3c6;个人专栏&#xff1a; 数 据 库 ⛳️ 功不唐捐&#xff0c;玉汝于成 目录 前言 正文 B树的特点和应用场景&#xff1a; B树相对于B树的优势&#xff1a; 结论&#xff1a; 结语 我的其他博客 前言 在数据…

GPT-5、开源、更强的ChatGPT!OpenAI公布2024年计划

年终岁尾&#xff0c;正值圣诞节热闹气氛的OpenAI写下了2024年的发展清单。 OpenAI联合创始人兼首席执行官Sam Altman在社交平台公布&#xff0c;AGI&#xff08;稍晚一些&#xff09;、GPT-5、更好的语音模型、更高的费率限制&#xff1b; 更好的GPTs&#xff1b;更好的推理…

CSS 文字弹跳效果

鼠标移过去 会加快速度 <template><div class"bounce"><p class"text" :style"{animationDuration: animationDuration}">欢迎使用UniApp Vue3&#xff01;</p></div> </template><script> export d…

HTML与CSS

目录 1、HTML简介 2、CSS简介 2.1选择器 2.1.1标签选择器 2.1.2类选择器 2.1.3层级选择器(后代选择器) 2.1.4id选择器 2.1.5组选择器 2.1.6伪类选择器 2.2样式属性 2.2.1布局常用样式属性 2.2.2文本常用样式属性 1、HTML简介 超文本标记语言HTML是一种标记语言&…

小白也能轻松上手的ECharts 配置手册

&#x1f4e2; 鸿蒙专栏&#xff1a;想学鸿蒙的&#xff0c;冲 &#x1f4e2; C语言专栏&#xff1a;想学C语言的&#xff0c;冲 &#x1f4e2; VUE专栏&#xff1a;想学VUE的&#xff0c;冲这里 &#x1f4e2; CSS专栏&#xff1a;想学CSS的&#xff0c;冲这里 &#x1f4e2; …

【赠书第13期】边缘计算系统设计与实践

文章目录 前言 1 硬件架构设计 2 软件框架设计 3 网络结构设计 4 安全性、可扩展性和性能优化 5 推荐图书 6 粉丝福利 前言 边缘计算是一种新兴的计算模式&#xff0c;它将计算资源推向网络边缘&#xff0c;以更好地满足实时性、低延迟和大规模设备连接的需求。边缘计算…

QML —— 键盘输入示例(附完整源码)

示例效果 Keys 所有视觉基本体都支持通过“附加关键帧”属性进行关键帧处理。按键可以通过onPressed和onReleased信号属性进行处理。 信号属性有一个KeyEvent参数&#xff0c;名为event&#xff0c;其中包含事件的详细信息。如果键被处理&#xff0c;则event.accepted应设置为t…

利用STM32和可控硅控制220V加热电路

利用STM32和可控硅控制220V加热电路 Chapter1 利用STM32和可控硅控制220V加热电路一、错误原理图二、正确原理图 Chapter2 可控硅驱动芯片MOC3081/3061Chapter3 一个MOC3061的可控硅触发电路的分析Chapter4 可控硅的两种触发方式&#xff1a;移相触发和过零触发1、过零触发2、移…