决战排序之巅(一)

news2024/10/2 22:16:35

决战排序之巅

    • 插入排序
      • 直接插入排序 void InsertSort(int* arr, int n)
      • 希尔排序 void ShellSort(int* arr, int n)
      • 测试插入排序
        • 测试函数 void verify(int* arr, int n)
        • 测试 InsertSort
        • 测试 ShellSort
        • 测试速度 InsertSort & ShellSort
    • 选择排序
      • 直接选择排序 void SelectSort(int* arr,int n)
      • 堆排序 void HeapSort(int* arr,int n)
        • 堆向下调整 void HeapDown(int* arr, int father,int size)
        • 堆排序 void HeapSort(int* arr,int n)
      • 测试选择排序
        • 测试 SelectSort
        • 测试 HeapSort
        • 测试速度 SelectSort & HeapSort
    • 希尔 VS 堆排 (Debug版本)
      • 说明
      • 1w rand( ) 数据测试
      • 10w rand( ) 数据测试
      • 10w rand( ) + i 数据测试
      • 100w rand( ) 数据测试
      • 100w rand( ) + i 数据测试
      • 1000w rand( ) 数据测试
      • 1000w rand( ) + i 数据测试
      • 测试代码如下:
    • 结语

欢迎来到决战排序之巅栏目,
本期我们将带来 插入排序(希尔) 与 选择排序(堆排) 的实现与比较

请添加图片描述
排序要常用的Swap函数(交换两个数值)

void Swap(int* a, int* b)
{
	int tmp = *a;
	*a = *b;
	*b = tmp;
}

插入排序

直接插入排序 void InsertSort(int* arr, int n)

基本思想:把待排序的记录按其关键码值的大小逐个插入到一个已经排好序的有序序列中,直到所有的记录插入完为止,得到一个新的有序序列。
插入流程如下所示:

当插入第i (i>=1)个元素时,前面的arr[0],arr[1],…,arr[i-1]已经排好序,此时用arr[i]的排序码arr[i-1],arr[i-2],…的排序码顺序进行比较,找到插入位置即将arr[i]插入,原来位置上的元素顺序后移。
代码如下:

void InsertSort(int* arr, int n)
{
	for (int i = 0; i < n - 1; i++)
	{
		int end = i;
		while (end >= 0)
		{
			if (arr[end + 1] < arr[end])
			{
				Swap(&arr[end + 1], &arr[end]);
				end--;
			}
			else
			{
				break;
			}
		}
	}
}

直接插入排序分析
特性:元素集合越接近与有序,直接插入排序算法的时间效率越高。
时间复杂度:O(N^2)
空间复杂度:O(N)
稳定性:稳定

希尔排序 void ShellSort(int* arr, int n)

希尔排序法又称缩小增量法
希尔排序法的基本思想是:先选定一个整数,把待排序文件中所有记录分成个组,所有距离为gap的记录分在同一组内,并对每一组内的记录进行排序。然后让堆gap重新取值,重复上述分组和排序的工作。当到达gap==1时,所有记录在统一组内排好序。
在这里插入图片描述
代码如下:

void ShellSort(int* arr, int n)
{
	int gap = n;
	while (gap > 1)
	{
		gap = gap / 3 + 1;

		for (int i = 0; i < n - gap; i++)
		{
			int end = i;
			while (end >= 0)
			{
				if (arr[end + gap] < arr[end])
				{
					Swap(&arr[end + gap], &arr[end]);
					end -= gap;
				}
				else
				{
					break;
				}
			}
		}
	}
}

希尔排序分析:
1.希尔排序是对直接插入排序的优化。
2.但gap > 1是程序对进行预排序,目的是使数组逐渐趋向于有序化,。当gap==1时数组就已经接近有序了,便可以很快的排好。对于整体而言,这样可以达到优化的效果。
3.希尔排序的gap取值有很多种取法,例如,最初Shell所提出的gap = [n/2] , gap = [gap/2],还有后来Knuth所提出的gap = [gap/3] + 1,还有人提出都取奇数,也有人提出各gap互质。但没有一种主张得到证明,因为Shell排序的时间度分析极其困难。在Knuth所著的**《计算机程序设计技巧》中利用大量试验资料得出,当n很大时,关键码平均比较次数和对象平均移动次数大约在 [ n 1.25 , 1.6 n 1.25 ] [ n^ {1.25} , 1.6n^{1.25}] [n1.25,1.6n1.25]范围内,这是 利用直接插入排序作为子序列方法 的情况下得到的。而我们以上代码的gap就是按照Knuth**提出的方式取值的。
稳定性:不稳定

测试插入排序

测试函数 void verify(int* arr, int n)
void verify(int* arr, int n)
{
	for (int i = 1; i < n; i++)
	{
		assert(arr[i] >= arr[i - 1]);
	}
}

以排非降序为例,若全为非降序则程序顺利通过,否则由assert函数终止程序并告知有误。

测试 InsertSort

我们先利用malloc开辟一个可存储10000个int类型的数组,再利用循环将数组内的数全置为随机数,再进行排序并检验。
在这里插入图片描述

我们运行后可以看到程序顺利通过,这说明测试成功,InsertSort正确无误。

测试 ShellSort

同理测试ShellSort.
在这里插入图片描述

可以看到ShellSort也是正确无误的。
测试代码:

void test_Sort()
{
	int n = 10000;
	int* arr = (int*)malloc(sizeof(int) * n); 
	assert(arr);
	for (int i = 0; i < n; i++)
	{
		arr[i] = rand();
	}
	ShellSort(arr, n);
	verify(arr, n);
}

int main()
{
	srand((unsigned int)time(NULL));
	test_Sort();
	return 0;
}
测试速度 InsertSort & ShellSort

先写一个numcreate函数来开辟空间。

int* numcreate(int n)
{
	int* arr = (int*)malloc(sizeof(int) * n);
	assert(arr);
	return arr;
}

开辟两个可储存10w int类型的数组,并利用rand( )函数为他们附上相同的值,再利用clock()函数来记录时间,最后比较即可。
在这里插入图片描述
我们可以看到插入排序用了5512μs,而希尔排序只用了13μs,所以恭喜ShellSort在速度上战胜了InsertSort,代码如下:

test()
{
	int n = 100000;
	int* arr1 = numcreate(n);
	int* arr2 = numcreate(n);

	for (int i = 0; i < n; i++)
	{
		arr2[i] = arr1[i] = rand();
	}

	int begin1 = clock();
	InsertSort(arr1, n);
	int end1 = clock();

	int begin2 = clock();
	ShellSort(arr2, n);
	int end2 = clock();

	printf("Insertsort : %d\n", end1 - begin1);
	printf("ShellSort  : %d\n", end2 - begin2);

	free(arr1);
	free(arr2);
}

选择排序

直接选择排序 void SelectSort(int* arr,int n)

基本思想:每次从待排数据中选出最小(最大)的值,再将其与起始位置的值交换,如此反复直到待排数据排完为止。
优化思路:每次选出最大值和最小值,最大值与待排数据末尾交换,最小值与待排数据起始位置交换,再反复循环即可。

实现步骤:
1.先确定数据开始位置begin与结束位置end
2.利用for循环找到[begin,end]区间的最大最小值,再分别交换,之后更新beginend
3.利用while循环来判断待排数据完成的条件
4.需要注意的是:当最大值为begin时,我们在交换时先交换了minibegin位置的数据,所以在进行maxiend前,我们要对maxi重新赋值,因为最大值被交换到了mini的位置,所以要maxi = mini

void SelectSort(int* a,int n)
{
	int begin=0,end=n-1;
	while(begin<end)
	{
		int maxi=begin,mini=end;
		for(int i=begin;i<=end;i++)
		{
			if(a[maxi]<a[i])
			{
				maxi=i;
			}
			if(a[mini]>a[i])
			{
				mini=i;
			}
		}
		Swap(&a[begin],&a[mini]);
		if(begin==maxi) 
		{
			maxi=mini;
		}
		Swap(&a[end],&a[maxi]);
		
		end--;
		begin++;
	}
}

直接选择排序分析:
特性:思路通俗易懂,但效率不高,且实际应用不高
时间复杂度:O(N^2)
空间复杂度:O(1)
稳定性:不稳定

堆排序 void HeapSort(int* arr,int n)

概念:堆排序是利用堆这种数据结构所设计的一种算法结构,通过逐个比较自身节点与左右子节点的大小来进行选择排序,这是选择排序的一种,它是通过堆来进行选择数据的。
方法:排升序建大堆,排降序建小堆。(本篇文章以排升序为例)
代码如下:

堆向下调整 void HeapDown(int* arr, int father,int size)

这里的size表示要调整数组的结束下标,father代表父节点即开始调整的位置,arr代表要调整的数组。

void HeapDown(int* arr, int father,int size)
{
	int child = father * 2 + 1;
	while (child < size)
	{
		if (child + 1 < size && arr[child + 1] > arr[child])
		{
			child++;
		}
		if (arr[father] < arr[child])
		{
			Swap(&arr[father], &arr[child]);
			father = child;
			child = child * 2 + 1;
		}
		else
		{
			break;
		}
	}
}

先选出最大的子节点,在与父亲进行比较,若父节点小于子节点则进行交换,直到子节点要小于父节点的值,或者child>=size即子节点的下标值大于size结束下标的值就跳出循环。

堆排序 void HeapSort(int* arr,int n)

利用大堆的特性,堆顶一定为堆中的最大值,所以我们可以利用循环取出堆顶与堆中的最后一个数进行交换,在向下调整堆中 0 ~ n-1-i的数据位置,使得堆顶又重新变成下标为0~n-1-i 时的最大值,在依次循环,最后就排好了一个升序。
代码如下:

void HeapSort(int* arr, int n)
{
	int i = 0;
	for (i = (n - 1 - 1) / 2 ; i >=0 ; i--)
	{
		HeapDown(arr, i, n);
	}
	//建堆

	for (i = 0; i < n - 1 ; i++)
	{
		Swap(&arr[0], &arr[n - i - 1]);
		HeapDown(arr, 0, n - i - 1);
	}
	//排序
}

堆排序:
特点:堆排序利用堆来选择数据进行排序,这样效率就快很多了。
时间复杂度:O(N*logN)
空间复杂度:O(1)
稳定性:不稳定

测试选择排序

测试 SelectSort

相同的方法测试10w个 数据,成功。

测试 HeapSort

在这里插入图片描述
相同的方法测试100w个 数据,成功。

void test_Sort()
{
	int n = 1000000;
	int* arr = (int*)malloc(sizeof(int) * n); 
	for (int i = 0; i < n; i++)
	{
		arr[i] = rand();
	}
	HeapSort(arr, n);
	verify(arr, n);
}

int main()
{
	srand((unsigned int)time(NULL));
	test_Sort();
	return 0;
}

测试速度 SelectSort & HeapSort

希尔 VS 堆排 (Debug版本)

说明

以下会分别对1w,10w,100w,1000w的数据进行100次的排序比较,并计算出排一趟的平均值。

rand( ) 生成随机数:rand( )函数生成的随机数区间为[0 , 32767] , rand()在10w以上量级的数据中会有较多的重复项。
rand( ) + i 生成随机数:它可以有效地避免rand( )在10w以上量级生成区间的问题,但是随着 i 越大,它生成的整体来看是较为有序的。

介绍就到这里了,让我们来看看这100次排序中,谁才是你心目中的排序呢?
PS:100次只是一个小小的测试数据,有兴趣的朋友可以在自己电脑上测试更多的来比较哦。

1w rand( ) 数据测试

在这里插入图片描述

10w rand( ) 数据测试

在这里插入图片描述

10w rand( ) + i 数据测试

在这里插入图片描述

100w rand( ) 数据测试

在这里插入图片描述

100w rand( ) + i 数据测试

在这里插入图片描述

1000w rand( ) 数据测试

在这里插入图片描述

1000w rand( ) + i 数据测试

在这里插入图片描述

测试代码如下:

int* numcreate(int n)
{
	int* arr = (int*)malloc(sizeof(int) * n);
	assert(arr);
	return arr;
}

void Ultimate_Test()
{
	int n = 10000000, count = 100;
	int timeShell = 0, timeHeap = 0;
	for (int a = 0; a < count; a++)
	{
		int* arr1 = numcreate(n);
		int* arr2 = numcreate(n);
		for (int i = 0; i < n; i++) 
			arr1[i] = arr2[i] = rand() + i;

		int begin1 = clock();
		ShellSort(arr1, n);
		int end1 = clock();

		int begin2 = clock();
		HeapSort(arr2, n);
		int end2 = clock();

		timeShell += end1 - begin1;
		timeHeap += end2 - begin2;

		free(arr1);
		free(arr2);
	}

	printf("ShellSort : %.2f\n", 1.0 * timeShell / count);
	printf("HeapSort  : %.2f\n", 1.0 * timeHeap / count);

}

int main()
{
	srand((unsigned int)time(NULL));
	Ultimate_Test();
	return 0;
}


结语

看完之后,谁才是你心目中的排序呢?
欢迎留言,让我们一起来期待在下一期 《决战排序之巅(二)》

以上就是本期的全部内容喜欢请多多关注吧!!!

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

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

相关文章

springboot3.0更新后,idea创建springboot2.x项目

springboot3.0更新后&#xff0c;idea创建springboot2.x项目 点击以下红色框中的按钮 出现了如下图所示&#xff1a; 到这里我们发现没有jdk8的版本&#xff0c;不要慌&#xff0c;我们可以先在这里选择21&#xff0c;然后进入到真正的项目中手动去修改这个jdk的版本&#xff0…

web:[GXYCTF2019]BabyUpload(文件上传、一句话木马、文件过滤)

题目 页面显示为文件上传 随便上传一个文件看看 上传一个文本文件显示 上传了一个图片显示 上传包含一句话木马的图片 上传了一个包含php一句话木马的文件&#xff0c;显示如上 换一个写法 上传成功 尝试上传.htaccess&#xff0c;上传失败&#xff0c;用抓包修改文件后缀 …

远程服务器——如何在Conda中安装R环境

目录 1. R的安装2. VScode 配置参考文献 1. R的安装 推荐使用anaconda或者miniconda&#xff0c;创建虚拟环R_env境然后安装R&#xff1b; 使用conda search r-base查看可下载的R的版本&#xff1b;R版本比较低&#xff0c;一般可以先增加源&#xff1a; % 增加源 conda con…

解决idea 通过build project 手动触发热部署失败

在debug运行项目的过程中&#xff0c;并且保证&#xff08;不添加方法&#xff0c;不修改方法名&#xff09;一定的规则的情况下&#xff0c;可以通过build project 来手动热部署项目&#xff0c;也就是会交换class文件与resouces文件。 设置项 Edit Configurations Modify Op…

保姆级 | XSS Platform环境搭建

0x00 前言 XSS Platform 平台主要是用作验证跨站脚本攻击。该平台可以部署在本地或服务器环境中。我们可以使用 XSS Platfrom 平台搭建、学习或验证各种类型的 XSS 漏洞。 0x01 环境说明 HECS(云耀云服务器)xss platformCentOS 8.0Nginx 1.24.0MySQL 5.6.51Pure-Ftpd 1.0.49ph…

TCP通讯

TCP通信 TCP通信方式呢 主要的通讯方式是一对一的通讯方式&#xff0c;也有着优点和缺点 它的优点对比于UDP来说就是更可靠 因为它的通讯方式是需要先发送消息 看看客户端是否能够接收到消息 如果没有回复消息的话 服务端 就不会发出文件 等待客户端回复消息&#xff0c;这…

听GPT 讲Rust源代码--src/tools(8)

File: rust/src/tools/rust-analyzer/crates/ide-assists/src/handlers/add_missing_match_arms.rs 在Rust源代码中&#xff0c;rust-analyzer是一个Rust编程语言的语言服务器。它提供了代码补全、代码重构和代码导航等功能来帮助开发者提高编码效率。 在rust-analyzer的代码目…

深度解析HarmonyOS开发-活动召集令元服务【鸿蒙北向应用开发实战】

目录 一&#xff0e;元服务和ArkTS语言简介1.1 学习元服务1.2 元服务带来的变革1.3 元服务全场景流量入口1.4 ArkTS学习1.5 ArkTS特点 二&#xff0e;DevEco Studio开发工具2.1 DevEco Studio学习2.2 DevEco Studio的主要特性2.3 端云一体化开发2.3.1端云一体化开发特点 2.4 低…

华为OD机试 - 攀登者2(Java JS Python C)

题目描述 攀登者喜欢寻找各种地图,并且尝试攀登到最高的山峰。 地图表示为一维数组,数组的索引代表水平位置,数组的元素代表相对海拔高度。其中数组元素0代表地面。 例如:[0,1,2,4,3,1,0,0,1,2,3,1,2,1,0],代表如下图所示的地图,地图中有两个山脉位置分别为 1,2,3,4,5…

如何在Spring Boot中集成RabbitMQ

如何在Spring Boot中集成RabbitMQ 在现代微服务架构中&#xff0c;消息队列&#xff08;如RabbitMQ&#xff09;扮演了关键的角色&#xff0c;它不仅能够提供高效的消息传递机制&#xff0c;还能解耦服务间的通信。本文将介绍如何在Spring Boot项目中集成RabbitMQ&#xff0c;…

项目架构-六边形架构的概述和实现

使用传统的分层架构&#xff0c;我们的所有依赖项都指向一个方向&#xff0c;上面的每一层都依赖于下面的层。传输层将依赖于交互器&#xff0c;交互器将依赖于持久层。 在六边形架构中&#xff0c;所有依赖项都指向内部——我们的核心业务逻辑对传输层或数据源一无所知。尽管如…

【推荐系统】了解推荐系统的生态(重点:推荐算法的主要分类)

【大家好&#xff0c;我是爱干饭的猿&#xff0c;本文重点介绍推荐系统的关键元素和思维模式、推荐算法的主要分类、推荐系统常见的问题、推荐系统效果评测。 后续会继续分享其他重要知识点总结&#xff0c;如果喜欢这篇文章&#xff0c;点个赞&#x1f44d;&#xff0c;关注一…

【webpack】应用篇

基础应用 代码分离常用的代码分离方法方法一&#xff1a;配置入口节点方法二&#xff1a;防止重复方法三&#xff1a;动态导入 缓存原因解决思路 缓存第三方库原因解决思路 将所有js文件单独存放文件夹拆分开发环境和生产环境配置公共路径环境变量和区分环境代码压缩 拆分配置文…

2023五岳杯量子计算挑战赛数学建模思路+代码+模型+论文

目录 计算力网络&#xff08;CPN&#xff09;是一种新型的信息基础设施&#xff0c;完整论文代码见文末 问题描述 2.1 问题1 2.2 问题2 2.3 问题3 问题1的解答过程&#xff1a; 问题3的解答过程&#xff1a; 决策优化应用场景&#xff1a;人工智能模型超参数调优 背景信…

HarmonyOS系统架构及项目结构浅析

语雀知识库地址&#xff1a;语雀HarmonyOS知识库 飞书知识库地址&#xff1a;飞书HarmonyOS知识库 基本概念 UI框架 HarmonyOS提供了一套UI开发框架&#xff0c;即方舟开发框架&#xff08;ArkUI框架&#xff09;。方舟开发框架可为开发者提供应用UI开发所必需的能力&#xf…

深度学习实战66-基于计算机视觉的自动驾驶技术,利用YOLOP模型实现车辆区域检测框、可行驶区域和车道线分割图

大家好,我是微学AI,今天给大家介绍一下深度学习实战66-基于计算机视觉的自动驾驶技术,利用YOLOP模型实现车辆区域检测框、可行驶区域和车道线分割图。本文我将介绍自动驾驶技术及其应用场景,并重点阐述了基于计算机视觉技术下的自动驾驶。自动驾驶技术是一种利用人工智能和…

在jupyter notebook中修改其他文件的解决方案

大家好,我是爱编程的喵喵。双985硕士毕业,现担任全栈工程师一职,热衷于将数据思维应用到工作与生活中。从事机器学习以及相关的前后端开发工作。曾在阿里云、科大讯飞、CCF等比赛获得多次Top名次。现为CSDN博客专家、人工智能领域优质创作者。喜欢通过博客创作的方式对所学的…

探索Vue小程序框架的底层原理

最近晚上有时间复盘之前研究小程序框架的相关内容&#xff0c;总结文章记录一下。 本篇文章主要介绍百度19年开源的Mars小程序开发框架&#xff0c;和Taro、mpvue、uniapp类似&#xff0c;都是编译型小程序框架&#xff0c;都是通过将 Vue 或 React 源码直接编译为小程序源码&a…

基于FPGA的温度控制系统设计(论文+源码)

1.系统设计 本次基于FPGA的智能温度控制系统&#xff0c;以FPGA为控制核心&#xff0c;采用自顶向下的设计方法&#xff0c;按照模块化设计的思路分别实现各个模块&#xff0c;再加以整合实现整个系统&#xff0c;从而达到了温度控制的目的。系统以水箱为被控对象&#xff0c;…

深入探索C语言中的二叉树:数据结构之旅

引言 在计算机科学领域&#xff0c;数据结构是基础中的基础。在众多数据结构中&#xff0c;二叉树因其在各种操作中的高效性而脱颖而出。二叉树是一种特殊的树形结构&#xff0c;每个节点最多有两个子节点&#xff1a;左子节点和右子节点。这种结构使得搜索、插入、删除等操作…