数据结构-排序3(终章)

news2024/12/29 10:18:31

前言:

上一章,对交换排序的冒牌和快排做了复盘,这一章对,归并排序以及非比较排序中的计数排序做一个复盘。


目录

2.4归并排序

2.4.1规定递归

2.4.2归并非递归

2.5非比较排序 

2.5.1计数排序 

2.6排序的稳定性分析

2.6.1冒泡排序

2.6.2 简单选择排序

2.6.3 直接插入排序

2.6.4希尔排序

2.6.5堆排序

 2.6.6归并排序

2.6.7快速排序



2.4归并排序

2.4.1规定递归

基本思想:

归并排序(MERGE-SORT)是建立在归并操作上的一种有效的排序算法,该算法是采用分治法(Divide andConquer)的一个非常典型的应用。将已有序的子序列合并,得到完全有序的序列;即先使每个子序列有序,再使子序列段间有序。若将两个有序表合并成一个有序表,称为二路归并。动态图如下所示:

 

 图解如下:


 

 红色的方块,代表已经分治到最后一层,要开始归并,归并如果在原数组上进行,会出现数值被覆盖的情况,所以需要重新开辟一个等大小的数组代码如下:

	// 开辟拷贝数组
	int* temp = malloc(sizeof(int) * n );
	if (temp == NULL)
	{
		perror("malloc");
		exit(-1);
	}

归并思想,就是比价两个元素的大小,按顺序放置进数组,然后将新数组拷贝回原数组,代码如下:

//开始归并
	int begin1 = begin, end1 = mid;
	int begin2 = mid + 1, end2 = end;

	int i = begin;//为了记住需要拷贝到temp数组的位置 要和原数组一一对应。
	while (begin1<=end1 && begin2<=end2)
	{
		if (a[begin1] < a[begin2]) //判断清楚 是下标还是数值 弟弟
		{
			temp[i++] = a[begin1++];
		}
		else 
		{
			temp[i++] = a[begin2++];
		}

	}
	while (begin1 <= end1)
	{
		temp[i++] = a[begin1++];
	}
	while (begin2 <= end)
	{
		temp[i++] = a[begin2++];
	}
	memcpy(a + begin, temp + begin, sizeof(int) * (end - begin + 1));//拷贝回原数组return 上一级递归 再次归并

}

归并排序也是利用递归的思想,这一层比较完后,返回上一层,继续完成相同的操作,但是,由于开辟了新数组,所以我们将递归在子函数中完成,具体代码如下:

void _MergeSort(int* a, int begin, int end, int* temp)
{
	//结束递归条件
	if (begin >= end)
	{
		return;
	}
	int mid = (begin + end) / 2;

	_MergeSort(a, begin, mid, temp);
	_MergeSort(a, mid+1, end,temp);
//归并
}

复杂度分析:

归并排序采用的也是分治的思想,二分比较,所以时间复杂度也为O(n*logn)

快排和归并以及堆时间复杂度都是一个量级的到底他们谁更优越呢,我们可以通过一个程序,来进行比较看看他们从开始运行到结束,花费了多长时间,具体代码如下:

void TestOP()
{
	srand(time(0));
	const int N = 100000;
	int* a1 = (int*)malloc(sizeof(int) * N);
	int* a2 = (int*)malloc(sizeof(int) * N);
	int* a3 = (int*)malloc(sizeof(int) * N);
	int* a4 = (int*)malloc(sizeof(int) * N);
	int* a5 = (int*)malloc(sizeof(int) * N);
	int* a6 = (int*)malloc(sizeof(int) * N);
	int* a7 = (int*)malloc(sizeof(int) * N);

	for (int i = 0; i < N; ++i)
	{
		a1[i] = rand();
		a2[i] = a1[i];
		a3[i] = a1[i];
		a4[i] = a1[i];
		a5[i] = a1[i];
		a6[i] = a1[i];
		a7[i] = a1[i];
	}

	int begin1 = clock();
	//InsertSort(a1, N);
	int end1 = clock();

	int begin2 = clock();
	//ShellSort(a2, N);
	int end2 = clock();

	int begin3 = clock();
	//SelectSort(a3, N);
	int end3 = clock();

	int begin4 = clock();
	HeapSort(a4, N);
	int end4 = clock();

	int begin5 = clock();
	QuickSort1(a1, 0, N - 1);
	int end5 = clock();

	int begin6 = clock();
	//MergeSort(a6, N);
	int end6 = clock();

	int begin7 = clock();
	//BubbleSort(a7, N);
	int end7 = clock();

	printf("InsertSort:%d\n", end1 - begin1);
	printf("ShellSort:%d\n", end2 - begin2);
	printf("SelectSort:%d\n", end3 - begin3);
	printf("HeapSort:%d\n", end4 - begin4);
	printf("BubbleSort:%d\n", end7 - begin7);
	printf("QuickSort:%d\n", end5 - begin5);
	printf("MergeSort:%d\n", end6 - begin6);

	free(a1);
	free(a2);
	free(a3);
	free(a4);
	free(a5);
	free(a6);
}

为了方便观察,我先屏蔽其他不相关,只保留需要比较的三个,最好将终端调制release版下,运行结果如下 :

还是快排更快一点,怪不得它叫快排。

2.4.2归并非递归

归并递归排序思想是 分治后层层递归,很像斐波那契,F(N) = F(N - 1) + F(N - 2) 求 F(N) 只要求得 F(N - 1 ) 和 F(N - 2)  一层层 递归到 F(1) 。如果我们不用递归,逆过来用F(1) + F(2) + ....F(N - 1) 同样可以得到F(N ) 

同样归并非递归,我们也可以逆过来,一个个归并,两个两个归并,四四归并最后归并成有序数组,但是这是有问题的如果数组元素个数是奇数的话,会出现越界情况,如下代码:

void MergeNorSort(int* a, int n)
{
	// 开辟拷贝数组
	int* temp = malloc(sizeof(int) * n);
	if (temp == NULL)
	{
		perror("malloc");
		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;
			printf("[%d %d],[%d,%d]", begin1, end1, begin2, end2);
			//归并
			int j = i;
			//if (end1 >= n || begin2 >= n)
			//{
			//	break;
			//}
			//if (end2 >= n)
			//{
			//	end2 = n - 1;
			//}
			while (begin1 <= end1 && begin2 <= end2)
			{
				if (a[begin1] < a[begin2]) //判断清楚 是下标还是数值 弟弟
				{
					temp[j++] = a[begin1++];
				}
				else
				{
					temp[j++] = a[begin2++];
				}

			}
			while (begin1 <= end1)
			{
				temp[j++] = a[begin1++];
			}
			while (begin2 <= end2)
			{
				temp[j++] = a[begin2++];
			}
			memcpy(a + i, temp + i, sizeof(int) * (end2 - i + 1));
			
		}
		printf("\n");
		gap *= 2;
	}
	free(temp);

运行结果如下:

 会出现,归并的边界下标出现越界的情况,如何对其进行改进呢,这时候需要分类分析:

如果是 begin2或者end1越界了,那这层就不用归并了,直接跳出本次归并的循环,如果,只是end2越界了的话,我们可以修改,下标,让其为n - 1,继续归并,图解如下所示:

 代码优化如下:

			if (end1 >= n || begin2 >= n)
			{
				break;
			}
			if (end2 >= n)
			{
				end2 = n - 1;
			}

运行结果如下:

2.5非比较排序 

非比较排序有很多,基数排序,计数排序,桶排序,等 我主要复盘一下,计数排序

2.5.1计数排序 

计数排序,是利用计数原理,对需要排序的数组,进行遍历计数,统计次数后,相对映射到新开辟的数组,然后排序,图解如下:

代码如下: 

//计数排序
void CountSort(int* a, int n)
{
	//选出最大数和最小数
	int max = a[0], min = a[0];
	for (int i = 0; i < n; i++)
	{
		if (a[i] <= min)
		{
			min = a[i];
		}
		if (a[i] >= max)
		{
			max = a[i];
		}
	}
	//相对大小
	int range = max - min + 1;
	//重新开辟一个数组 用来记录 不同元素出现的个数
	int* countA = (int *)calloc(range,sizeof(int));
	if (countA == NULL)
	{
		perror("calloc:fail");
		exit(-1);
	}
	//将 数组a中元素通过相对位置 计算出重复个数
	for (int i = 0; i < n; i++)
	{
		countA[a[i] - min]++;
	}

	int j = 0;
	for (int i = 0; i < range; i++)
	{
		while (countA[i]--)
		{
			a[j++] = i + min;
		}
	}

	free(countA);
}

 总结:计数排序适合范围比较集中,且范围不大的整形数组,不适合范围比较分散,且范围很大的非整形数,例如 浮点数,字符串。

2.6排序的稳定性分析

稳定性:假定在待排序的记录序列中,存在多个具有相同的关键字的记录,若经过排序,这些记录的相对次序保持不变,即在原序列中,r[i]=r[j],且r[i]在r[j]之前,而在排序后的序列中,r[i]仍在r[j]之前,则称这种排序算法是稳定的;否则称为不稳定的,例如:一个数组中 出现前后两个相同的数,排序完成后,前后相同的元素位置不变。如下图所示:

2.6.1冒泡排序

稳定性:很稳,相同的元素不会发生交换。

2.6.2 简单选择排序

稳定性:不稳定,寻找到需要的元素 可能不会影响到本身元素的稳定性,但是会影响到别的元素的位置。如下图所示:

2.6.3 直接插入排序

稳定性:很稳定

2.6.4希尔排序

稳定性:不稳定,相同元素会在预排序的过程分到不同组中,导致,相同元素的前后位置发生变化。

2.6.5堆排序

稳定性:不稳定,交换根和最后一个元素 就可能会影响元素位置变化,如下如所示:

 2.6.6归并排序

稳定性:很稳,只要在程序中将begin1和begin2相等的时候,归并begin1就可以了。

2.6.7快速排序

稳定性:不稳定, 

 

 总结成一个表格如下所示:

 

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

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

相关文章

【Transformer系列(2)】注意力机制、自注意力机制、多头注意力机制、通道注意力机制、空间注意力机制超详细讲解

前言 注意力机制一直是一个比较热的话题&#xff0c;其实在很早之前就提出了&#xff0c;我们在学习图像分类时在SENet就见到过&#xff08;直通车&#xff1a;经典神经网络论文超详细解读&#xff08;七&#xff09;——SENet&#xff08;注意力机制&#xff09;学习笔记&…

金陵科技学院五年一贯制专转本管理学原理考试大纲

金陵科技学院五年一贯制专转本管理学原理考试大纲 一、考核对象 本课程的考核对象为五年一贯制高职专转本“旅游管理”专业入学考试考生。 二、考核方式 本课程考核采用闭卷笔试的方式。 三、命题依据及原则 1、命题依据 参考书目&#xff1a;《管理学——原理与方法》 …

Docker Swarm集群企业案例实战

1. Docker Swarm集群企业案例实战 Docker Swarm 和 Docker Compose 一样&#xff0c;都是 Docker 官方容器编排项目&#xff0c;但不同的是&#xff0c;Docker Compose 是一个在单个服务器或主机上创建多个容器的工具&#xff0c;而 Docker Swarm 则可以在多个服务器或主机上创…

驼峰式匹配-力扣1023-java

一、题目描述 如果我们可以将小写字母插入模式串 pattern 得到待查询项 query&#xff0c;那么待查询项与给定模式串匹配。&#xff08;我们可以在任何位置插入每个字符&#xff0c;也可以插入 0 个字符。&#xff09; 给定待查询列表 queries&#xff0c;和模式串 pattern&a…

未来技术方向——“乐高式”可组装式开发能力

技术正在改变各行各业的发展&#xff0c;Gartner的主要战略技术趋势一直是行业的技术风向标之一。近3年&#xff0c;Gartner在主要的战略技术趋势中都提到组装式技术&#xff0c;2021年首次提出组装式企业&#xff0c;2022年提出可组装式应用&#xff0c;2023年在2项主要战略技…

ModuleNotFoundError: No module named ‘d2l’

目录 1. 下载李沐老师分享的源代码 step1&#xff1a;下载李沐老师分享的源代码&#xff1a; step3&#xff1a;Anaconda Prompt中安装d2l(这个l是英文) step4&#xff1a;运行代码&#xff0c;成功&#xff1a; &#xff08;番外&#xff09;ModuleNotFoundError: No mod…

【微服务】5、声明式 HTTP 客户端 —— Feign

目录 一、RestTemplate 不好的地方二、Feign 是什么三、使用四、自定义 Feign 的配置(1) Feign 的几个常见配置(2) 配置 Feign 的日志级别① 通过配置文件② Java 代码配置日志级别 五、Feign 性能优化(1) 性能优化介绍(2) 修改 Feign 底层的 HTTP 请求客户端 六、Feign 的最佳…

C++:std::function模板类(前言):为什么有了函数指针还需要Functional

为什么有了函数指针还有 Functional 1: 函数指针定义2&#xff1a; 函数指针结论3&#xff1a;疑问4&#xff1a; Function来源5&#xff1a;Functional 特点 1: 函数指针定义 在C中可以使用指针指向一段代码&#xff0c;这个指针就叫函数指针&#xff0c;假设有下面一段代码 …

交友项目【首页推荐,今日佳人,佳人信息】

目录 1&#xff1a;首页推荐 1.1&#xff1a;接口地址 1.2&#xff1a;流程分析 1.3&#xff1a;代码实现 2&#xff1a;今日佳人 1.1&#xff1a;接口地址 1.2&#xff1a;流程分析 1.3&#xff1a;代码实现 3&#xff1a;佳人信息 1.1&#xff1a;接口地址 1.2&am…

计算机基础--MySQL--索引

参考文献 [MySQL索引连环18问&#xff01;] https://zhuanlan.zhihu.com/p/364041898[深入理解MySQL索引] https://www.infoq.cn/article/ojkwyykjoyc2ygb0sj2c[聚集索引和非聚集索引的区别] https://juejin.cn/post/7001094401858469918[索引分类] https://blog.csdn.net/dd2…

【消费战略】解读100个食品品牌丨王小卤 4年10亿爆品破局

爆品破局 王小卤的聚焦发展! 王小卤创建于 2016 年&#xff0c;与饮料行业的独角兽元气森林同年。 相较于元气森林的快速增长&#xff0c;王小卤历经 三年坎坷之路&#xff0c;直至 2019 年才踏上高增长的赛道&#xff0c;实现四年十亿的增长。 “所有的消费品都值得重新 做…

RHCSA练习作业(二)

目录 题目一 题目二 题目三 第四题 第五题 题目一 文件查看&#xff1a;查看/opt/passwd文件的第六行&#xff08;使用head和tail指令&#xff09; 代码如下&#xff1a; head -6 /opt/passwd | tail -1 题目二 在/etc及其子目录中&#xff0c;查找host开头的文件&#x…

纯净Python环境的安装以及配置PyCharm编辑器

前言 优质的教程可以让我们少走很多弯路&#xff0c;这一点毋庸置疑。去年二月我接触了Python&#xff0c;并找到了一份优质的配置教程&#xff0c;让我能够快速上手Python的学习。现在&#xff0c;一年多过去了&#xff0c;我已经有了很大的进步和变化&#xff0c;这也让我更…

ARM kernel 内核的移植 - 从三星官方内核开始移植

一、内核移植初体验 1、三星官方移植版内核获取 (1) 从网盘下载源码包。 (2) 这个文件最初是来自于三星的 SMDKV210 开发板附带的光盘资料。 2、构建移植环境 (1) Windows下建立工程。 (2) ubuntu下解压。 3、配置编译下载尝试 (1) 检查 Makefile 中 ARCH 和 CROSS_COMPI…

手把手教你Python爬虫

前言 python爬虫技术在java开发工作中属于工具性的技术属性&#xff0c;所以我这里就只从爬取一个网站的数据为例作为教学内容&#xff0c;具体的基础学习与其它的扩展知识内容&#xff0c;我会以链接的形式给出&#xff0c;若有兴趣可自行点击学习。 python基础知识教学 Pyth…

怎么压缩图片的体积大小,4款软件分享

怎么压缩图片的体积大小&#xff1f;因为在日常生活和工作中&#xff0c;我们常常会遇到需要压缩图片大小的情况。图片的大小是由像素点数量和每个像素的颜色深度共同决定的&#xff0c;一般来说&#xff0c;像素点数量越多&#xff0c;每个像素的颜色深度越高&#xff0c;图片…

四百元以内哪种耳机音质好?2023便宜音质好的蓝牙耳机推荐

现如今&#xff0c;蓝牙耳机的发展速度越来越快&#xff0c;不少人对于其音质方面的要求也越来越高。最近看到很多人问&#xff0c;有没有便宜音质又好的蓝牙耳机&#xff1f;针对这个问题&#xff0c;我来给大家推荐几款便宜音质好的蓝牙耳机&#xff0c;一起来看看吧。 一、…

怎么远程控制电脑

为什么要从另一台电脑远程控制电脑&#xff1f; 如今&#xff0c;Splashtop已广泛应用于各个领域。 在很多情况下&#xff0c;您需要从另一台远程电脑控制一台电脑。 这里演示了两个例子&#xff1a; 1&#xff1a;当您不在同一楼层时&#xff0c;您的同事需要您的帮助来解决…

阿里云张献涛:云原生计算基础设施助力汽车行业数字化升级

2023 年阿里云峰会北京站《云上智能汽车》论坛&#xff0c;阿里云智能基础产品部副总裁、阿里云智能弹性计算 & 无影产品线总经理张献涛&#xff0c;发表了《云原生计算基础设施助力汽车行业数字化升级》的主题演讲。 当前&#xff0c;汽车行业的数字化浪潮已经渗透到汽车设…

System V 共享内存

System V 共享内存 共享内存是什么如何使用共享内存ftokshmgetshmatshmdtshmctl 共享内存的原理共享内存实现两个进程间通信共享内存的特点共享内存与管道配合使用两个进程间通信多个进程间通信 共享内存是什么 &#x1f680;共享内存是最快的IPC形式。一旦这样的内存映射到共…