排序(3)【归并排序】【计数排序】【排序算法度及其稳定性分析】

news2024/11/19 20:26:17

一.归并排序

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

在八个数中进行排序就是这样的: 

 1.归并排序的实现

void MergeSort(int* a, int* tmp, int begin, int end)
{
	if (begin >= end)
		return;
	int mid = (begin + end) / 2;//求中间值,目的是为了不断的缩小区间
	MergeSort(a, tmp, begin, mid);//从左边开始,运用递归,一层一层的缩小区间
	MergeSort(a, tmp, mid + 1, end);//这里是右区间
	int begin1 = begin, end1 = mid;//[begin,mid]与[mid+1,end]
	int begin2 = mid + 1, end2 = end;
	int i = begin;//从begin开始
	while (begin1 <= end1 && begin2 <= end2)//左右区间的值依次开始比较,比较小的那个放到新的区间,下标为i
	{
		if (a[begin1] < a[begin2])
		{
			tmp[i++] = a[begin1++];
		}
		else
		{
			tmp[i++] = a[begin2++];
		}
	}
	//左右区间通过比较已经进入完成,接下来就是把剩下的值也输入进去
	while (begin1 <= end1)
	{
		tmp[i++] = a[begin1++];
	}
	while (begin2 <= end2)
	{
		tmp[i++] = a[begin2++];
	}
	//这里因为我们单独创建了一个数组,所以我们需要把tmp里面的值拷贝到原来的数组里
	memcpy(a + begin, tmp + begin, sizeof(int) * (end - begin + 1));
	//因为我们的区间不一定从哪里开始(begin不确定),我们需要知道我们要从哪里开始拷贝,所以这里加了一个begin
}

这个代码的思想就是,我们把一个区间通过一半一半的分,分成更小的区间,然后对这小区间进行排序,再把小区间和小区间合成一个大区间,其中的每一个小区间都是有序的。这就像是一个二叉树后序的过程。

2.用非递归实现归并排序

上面对我们都是使用递归来实现排序,当然肯定有人不喜欢递归来实现。这里还有一种非递归的方式:

void MergeSortNonR(int* a, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);//动态创建一个数组
	if (tmp == NULL)
	{
		perror("tmp::malloc");
		return;
	}
	int gap = 1;//gap指的是每次归并几个元素
	while (gap < n)//gap不能大于等于n
	{
		for (int i = 0; i < n; i += 2 * gap)//这里的循环是把数组里的元素全部进行归并,每组数据个数是gap,还要注意i += 2 * gap指的是跳到下一组
		{
			//然后开始分区间[begin1,end1][begin2,end2]
			//归并是两组两组进行的这一个区间里的值与下一个区间里的值进行归并
			int begin1 = i, end1 = i + gap - 1;
			int begin2 = i + gap, end2 = i + 2 * gap - 1;
			//这里需要判断三种特殊情况,后面单独说
			if (begin2 >= n)
			{
				break;
			}
			if (end2 >= n)
			{
				end2 = n - 1;
			}
			//为了不影响变量i,再创建一个变量,往tmp数组里入数据
			int j = i;
			while (begin1 <= end1&&begin2<=end2)//这里就是上面说过的入数据,左区间和右区间进行比较
			{
				if (a[begin1] < a[begin2])
				{
					tmp[j++] = a[begin1++];
				}
				else
				{
					tmp[j++] = a[begin2++];
				}
			}
			//这里就是把剩余的没有入完的数据都入进去
			while (begin1 <= end1)
			{
				tmp[j++] = a[begin1++];
			}
			while(begin2<=end2)
			{
				tmp[j++]=a[begin2++];
			}
			//因为我们创建的是临时变量tmp,所以这里用memcpy
			//每次排完两组,我们就拷贝数据,如果在for循环外部,会导致一些错误,后面说
			memcpy(a + i, tmp + i, sizeof(int) * (end2 - i + 1));
			
		}
		//到这里我们就已经完全的排完序了,下面就是继续增大区间,继续循环归并
		gap = gap * 2;
	}
	//还有就是别忘了释放
	free(tmp);
	tmp = NULL;
}

这里还有一些问题要说一下就是上面没有解释的那个代码:

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

如果我不加上面的两个if语句的话,我们会发现只有当我们的数据个数是2的n次方的时候才可以完成排序的功能。如果我有十个数的话,它就会有越界的风险。因为我每一次跳过的距离是gap,而gap是呈2的倍数增长。那么后面我在求区间的时候就会出现越界的问题。

有三种情况:

分别代表着end2越界,begin2和end2越界,end1,begin2和end2越界。 后面两种对应着第一个if语句:

if (begin2 >= n)
{
	break;
}

意思就是,如果我的begin2越界了,我就跳出这个循环,不再进行归并,因为后面的元素个数已经没有了,不需要进行归并,仅需完成前面归并的过程就行了。

然后就是第一种情况,我们只需要调整一下end2的值就行了。原本end2是越界的,我们直接让它成为最后一个元素就行了(end1之前也就是end2,在上一个过程中已经调整过了)。

如果将memcpy放在for循环外部,那么在第一次循环后就会把整个排序结果拷贝到原始数组中,导致后续的归并排序无法正确进行。比如我有11个数,那么在刚开始的时候,我有一个数就不能被拷贝进去,tmp里是10个值,此时我把tmp数组拷贝到原数组里就会出现出错。

3.归并排序的时间复杂度

归并排序的时间复杂度是O(nlogn),其中n是要排序的元素个数。归并排序的核心操作是将待排序的数组不断地分割成两个子数组,直到每个子数组只有一个元素。然后,将两个子数组合并成一个有序数组。这个分割和合并的过程需要重复logn次,每次操作的时间复杂度是O(n)。因此,归并排序的总时间复杂度是O(nlogn)。

空间复杂度是O(n)。

二.计数排序

1.计数排序的实现

这个排序是比较特殊一点的排序,它是不用比较大小就可以把数据排好的一种方式,它所用的方式是统计。

void CountSort(int* a, int n)
{
	//先找到数组里的最大值和最小值,求出数组里数据的范围
	int min = a[0];
	int max = a[0];
	for (int i = 1; i < n; i++)
	{
		if (a[i] < min)
			min = a[i];
		if (a[i] > max)
			max = a[i];
	}
	int range = max - min + 1;
	//根据范围的大小开辟空间,注意这里用的calloc直接也可以把开辟的空间里的值初始化为0
	int* count = (int*)calloc(range, sizeof(int));
	if (count == NULL)
	{
		perror("calloc fail");
		return;
	}
	//因为我们count数组里的值都是0,根据相对值,如果出现与count数组下标相同的值我们就加一
	for (int i = 0; i < n; i++)
	{
		count[a[i] - min]++;
	}
	//到这里就是排序的过程,根据count数组的下标,我们把它的下标值加上min就是我们需要排序的数,依次放进数组里
	int j = 0;
	for (int i = 0; i < range; i++)
	{
		while (count[i]--)
		{
			a[j++] = i + min;
		}
	}
	free(count);
}

可能稍微用了一点比较大小的方式,但是主体我们利用了下标统计的方式来实现排序。

这个排序逻辑没有那么复杂,效率也很快,但是唯一不足的是它只能排列整数,遇到小数,负数,结构体等等都不行了。

 2.计数排序的时间复杂度

计数排序的时间复杂度为O(n+k),其中n为待排序元素的个数,k为待排序元素的取值范围。在最坏情况下,计数排序的时间复杂度可以达到O(n^2),但是当k不大时,计数排序的时间复杂度一般是线性的,即O(n)。相比于其他排序算法,计数排序的时间复杂度较低,但是需要额外的空间来存储计数数组。

三.排序算法度及其稳定性分析

 注意这里的稳定性指的是相同的数相对位置不变。

到这里一些常见的排序算法就完全结束了。感谢大家的观看,如有错误还请多多指出。

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

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

相关文章

C++ 66 之 类模版

#include <iostream> #include <string> using namespace std;// 习惯性 < >中 类模板用class 普通的函数模板就用typename // template<class NAMETYPE, class AGETYPE> template<class NAMETYPE, class AGETYPE int> // 可以设置默认的类型值…

集团门户网站的设计

管理员账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;管理员管理&#xff0c;论坛管理&#xff0c;集团文化管理&#xff0c;基础数据管理&#xff0c;公告通知管理 前台账户功能包括&#xff1a;系统首页&#xff0c;个人中心&#xff0c;论坛&#xff0…

收银系统源码-千呼新零售2.0【线下促销】

千呼新零售2.0系统是零售行业连锁店一体化收银系统&#xff0c;包括线下收银线上商城连锁店管理ERP管理商品管理供应商管理会员营销等功能为一体&#xff0c;线上线下数据全部打通。 适用于商超、便利店、水果、生鲜、母婴、服装、零食、百货等连锁店使用。 详细介绍请查看下…

在LangChain中,LLM(大型语言模型)和LLM Chain的区别是什么?

简单来说&#xff0c;LLM是一个大型语言模型&#xff0c;而LLM Chain是由多个LLM或其他组件组成的链式结构&#xff0c;用于在LangChain中构建复杂的自然语言处理流程。 Direct LLM Interface: 直接大型语言模型&#xff08;LLM&#xff09;接口&#xff1a; llm Open…

【NOI-题解】1234. 任意输入一正整数N,要求把它拆成质因子的乘积。1446. 人口增长问题

文章目录 一、前言二、问题问题&#xff1a;1234. 任意输入一正整数N&#xff0c;要求把它拆成质因子的乘积。问题&#xff1a;1446. 人口增长问题 三、感谢 一、前言 本章节主要对循环应用的题目进行讲解&#xff0c;包括《1234. 任意输入一正整数N&#xff0c;要求把它拆成质…

防止员工离职导致数据泄露,员工离职后把文件带出公司

中科数安的电脑文件资料透明加密防泄密系统确实能够在一定程度上防止员工离职导致的数据泄露。以下是具体的分析&#xff1a; www.weaem.com 访问控制与权限管理&#xff1a;系统实施了严格的权限管理制度&#xff0c;对核心文件和数据资源进行细致的访问权限划分。这意味着&am…

【数据结构与算法】稀疏矩阵(三元组,十字链表存储)详解

给出稀疏矩阵的节省内存的存贮结构并写出相应的输入、输出算法。 稀疏矩阵是一个大部分元素为0的矩阵。为了节省内存&#xff0c;我们可以只存储非零元素。一种常见的存储结构是三元组&#xff0c;每个三元组包含一个非零元素的行索引、列索引和值。 #include <stdio.h>…

BC-Linux 8.6最小化安装的服务器启用GNOME图形化界面

本文记录了BC-Linux 8.6最小化安装的服务器如何启用GNOME图形化界面的过程。 一、服务器环境 1、系统版本 [rootlocalhost ~]# cat /etc/os-release NAME"BigCloud Enterprise Linux" VERSION"8.6 (Core)" ID"bclinux" ID_LIKE"rhel fe…

基于STM32和人工智能的智能小车系统

目录 引言环境准备智能小车系统基础代码实现&#xff1a;实现智能小车系统 4.1 数据采集模块4.2 数据处理与分析4.3 控制系统4.4 用户界面与数据可视化应用场景&#xff1a;智能小车管理与优化问题解决方案与优化收尾与总结 1. 引言 随着机器人技术的发展&#xff0c;智能小…

Android断点续传原理及实现

常见两种网络请求方式 一、 HttpURLConnection HttpURLConnection的setRequestProperty()方法&#xff0c;对我们要读取的字节部分进行控制&#xff0c;比如: 1.Range0-100代表只读取前100个字节。 2.Range100-500代表读取从第100个字节开始&#xff0c;读到第500个字节为止。…

教你如何安装 IntelliJ IDEA

安装 IntelliJ IDEA 的步骤通常如下&#xff0c;这里提供的是基于 Windows 系统的安装指南。 下载 IntelliJ IDEA 1. 访问 JetBrains 官方网站&#xff1a;[https://www.jetbrains.com/idea/download/](Download IntelliJ IDEA – The Leading Java and Kotlin IDE) 2. 选择适…

让AI代替我写代码???——Codeium安装及使用

序 不知不觉又到了期末大作业的时间了&#xff0c;面对老师布置的超繁琐代码项目&#xff0c;竟一时有些发怵&#xff0c;不知道从何下手才好…… 但是&#xff0c;懒惰如张同学的我怎么能拘泥于老老实实完成这些毫无技术可言的作业呢&#xff1f; 于是乎&#xff0c;我便寻…

【YOLOv9改进[注意力]】在YOLOv9中使用注意力CascadedGroupAttention(2023)的实践 + 含全部代码和详细修改方式

本文将进行在YOLOv9中使用注意力CascadedGroupAttention的实践,助力YOLOv9目标检测效果的实践,文中含全部代码、详细修改方式。助您轻松理解改进的方法。 改进前和改进后的参数对比: 目录 一 CascadedGroupAttention 二 在YOLOv9中使用注意力CascadedGroupAttention的实…

【操作系统】操作系统实验04-文件系统扩展

题目要求&#xff1a; 对【程序5_9】进行扩展&#xff0c;要求参数为目录名&#xff0c;且其下至少有三层目录&#xff0c;分别用深度遍历及广度遍历两种方法对此目录进行遍历&#xff0c;输出此目录下所有文件的大小及修改时间。 1. 程序代码&#xff08;注意程序格式&#…

SYD88xx使代码在RAM内存中执行/运行

SYD88xx使代码在RAM中执行 SYD8811/8810默认都是cache模式的&#xff0c;但是在代码首次运行的时候&#xff0c;需要将代码从flash搬到cache中执行&#xff0c;这样第一次的代码执行可能会比较慢&#xff0c;这里提供一个将需要提速的代码放到RAM中执行的方法。 对于SYD8811…

【操作系统】操作系统实验03-页面置换算法

题目要求&#xff1a; 先读懂实验文档中的两个页面置换算法&#xff0c;参考文档中程序&#xff0c;实现以下要求&#xff1a; 假设某个进程P有6个页面&#xff0c;进程访问页的顺序自拟&#xff08;不少于20个&#xff09;&#xff0c;在内存中分配给该进程4个页面&#xff…

Blazor 中基于角色的授权

介绍 Blazor用于使用 .NET 代码库创建交互式客户端 Web UI。Microsoft 默认在 Blazor 应用程序中提供了一个用于身份验证和授权的身份框架。请注意&#xff0c;他们目前使用 MVC Core Razor 页面作为身份验证 UI。使用“Microsoft.AspNetCore.Identity.UI”包库来实现这一点。…

数据库大作业——音乐平台数据库管理系统

W...Y的主页&#x1f60a; 代码仓库分享&#x1f495; 《数据库系统》课程设计 &#xff1a;流行音乐管理平台数据库系统&#xff08;本数据库大作业使用软件sql server、dreamweaver、power designer&#xff09; 目录 系统需求设计 数据库概念结构设计 实体分析 属性分…

白酒:中国的酒文化的传承与发扬

中国&#xff0c;一个拥有五千年文明史的国度&#xff0c;其深厚的文化底蕴孕育出了丰富多彩的酒文化。在这片广袤的土地上&#xff0c;酒不仅仅是一种产品&#xff0c;更是一种情感的寄托&#xff0c;一种文化的传承。云仓酒庄的豪迈白酒&#xff0c;正是这一文化脉络中的一颗…

低代码专题 | 低代码开发平台怎么收费,价格多少?一文揭秘!

低代码开发平台近几年真的火得一塌糊涂&#xff0c;不少企业都开始关注并尝试这种新的开发方式。 然而&#xff0c;关于低代码开发平台的收费问题&#xff0c;却是众说纷纭、信息零散。为了帮助大家更清晰地了解低代码开发平台的收费情况&#xff0c;这篇文章将进行全面的解读…