归排、计排深度理解

归并排序:是创建在归并操作上的一种有效的排序算法。算法是采用分治法(Divide and Conquer)的一个非常典型的应用,且各层分治递归可以同时进行。归并排序思路简单,速度仅次于快速排序,为稳定排序算法,一般用于对总体无序,但是各子项相对有序的数列。

1. 基本思想

归并排序是用分治思想,分治模式在每一层递归上有三个步骤:

  • 分解(Divide):将n个元素分成个含n/2个元素的子序列。
  • 解决(Conquer):用合并排序法对两个子序列递归的排序。
  • 合并(Combine):合并两个已排序的子序列已得到排序结果。

归并排序的特性总结:

1. 归并的缺点在于需要O(N)的空间复杂度,归并排序的思考更多的是解决在磁盘中的外排序题。
2. 时间复杂度:O(N*logN)
3. 空间复杂度:O(N)
4. 稳定性:稳定

 这是归并排序的主要概念。

归并排序有递归和非递归两种,我们首先来实现递归的代码

代码

//归并递归
void _MergeSore(int* arr, int left, int right, int* tmp)
{
	//递归结束条件
	if (left >= right)
		return;
	//int min = left + ((right - left) >> 1);
	int min = (left + right) / 2;
	//递归开始
	_MergeSore(arr, left, min, tmp);
	_MergeSore(arr, min + 1, right, tmp);
	//排序开始
	int begin1 = left, end1 = min;
	int begin2 = min + 1, end2 = right;
	int i = left;
	while (begin1 <= end1 && begin2 <= end2)
	{
		if (arr[begin1] < arr[begin2])
		{
			tmp[i++] = arr[begin1++];
			/*i++;
			begin1++;*/
		}
		if (arr[begin1] >= arr[begin2])
		{
			tmp[i++] = arr[begin2++];
			/*i++;
			begin2++;*/
		}
	}
	while (begin1 <= end1)
	{
		tmp[i++] = arr[begin1++];
	}
	while (begin2 <= end2)
	{
		tmp[i++] = arr[begin2++];
	}
	//将建立的数组拷贝到原数组中
	for (int i = 0; i <= right; i++)
	{
		arr[i] = tmp[i];
	}
}
//归并排序
void MergeSort(int* arr, int n)
{
	//先建立一个数组,用来存放排序的元素
	int* tmp = (int*)malloc(sizeof(int) * (n));
	if (tmp == NULL)
	{
		perror("perror,file");
		return;
	}
	//归并函数实现
	_MergeSore(arr, 0, n - 1, tmp);
	//销毁新建数组,防止内存泄漏
	free(tmp);
	//防止野指针
	tmp = NULL;
}

下面是非递归的写法,非递归的思想与递归的思想几乎一样,大家可以自己想下过程。

  1.  申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列
  2.  设定两个指针,最初位置分别为两个已经排序序列的起始位置
  3.  比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
  4.  重复步骤③直到某一指针到达序列尾
  5.  将另一序列剩下的所有元素直接复制到合并序列尾

void _MergeSoreNonR1(int* arr, int left, int right, int* tmp)
{
	int gap = 1;
	int i = 0;
	while (gap <= right)
	{
		for (i = 0; i <= right; i += 2 * gap)
		{
			//[i,I+gap-1]  [i+gap,2*gap-1]
			int begin1 = i, end1 = i + gap - 1;
			int begin2 = i + gap, end2 = i + 2 * gap - 1;
			//printf(" %d", end2);
			if (end1 > right)
				end1 = right;
			if (begin2 > right)
			{
				begin2 = right + 1;
				end2 = right;
			}
			if (end2 > right)
				end2 = right;
			int index = i;
			while (begin1 <= end1 && begin2 <= end2)
			{
				if (arr[begin1] < arr[begin2])
				{
					tmp[index++] = arr[begin1++];
				}
				if (arr[begin1] >= arr[begin2])
				{
					tmp[index++] = arr[begin2++];
				}
			}
			while (begin1 <= end1)
			{
				tmp[index++] = arr[begin1++];
			}
			while (begin2 <= end2)
			{
				tmp[index++] = arr[begin2++];
			}
		}

		for (i = 0; i <= right; i++)
		{
			arr[i] = tmp[i];
		}
		gap *= 2;
	}
}

void MergeSortNonR(int* arr, int n)
{
	int* tmp = (int*)malloc(sizeof(int) * n);
	if (tmp == NULL)
	{
		perror("malloc,file");
		return;
	}
	_MergeSoreNonR1(arr, 0, n-1, tmp);
	free(tmp);
	tmp = NULL;
}

下面来看计数排序

计数排序不用比较两个数的大小,它的做法是统计哪个元素出现的次数,然后通过这个元素出现的次数来排序。

计数算法只能使用在已知序列中的元素在0-k之间,且要求排序的复杂度在线性效率上。 Â 计数排序和基数排序很类似,都是非比较型排序算法。但是,它们的核心思想是不同的,基数排序主要是按照进制位对整数进行依次排序,而计数排序主要侧重于对有限范围内对象的统计。基数排序可以采用计数排序来实现。

计数排序的特性总结:
1. 计数排序在数据范围集中时,效率很高,但是适用范围及场景有限。
2. 时间复杂度:O(MAX(N,范围))
3. 空间复杂度:O(范围)
4. 稳定性:稳定

代码实现

void CountSort(int* arr, int n)
{
	//确定数组开辟的大小
	int max = arr[0], min = arr[0];
	for (int i = 1; i < n; i++)
	{
		if (arr[i] > max)
			max = arr[i];
		if (arr[i] < min)
			min = arr[i];
	}
	int range = max - min + 1;
	//建立一个数组
	int* count = (int*)malloc(sizeof(int) * range);
	if (count == NULL)
	{
		perror("malloc file");
		return NULL;
	}
	memset(count, 0, sizeof(int) * range);
	for (int i = 0; i < n; i++)
	{
		count[arr[i]-min]++;
	}
	int j = 0;
	for (int i = 0; i < n; i++)
	{
		while (count[i]--)
		{
			arr[j] = i+min;
			j++;
		}
	}
	free(count);
	count = NULL;
}

 下面是一张八大排序的比较图

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

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

相关文章

银行数字化转型导师坚鹏:数字化思维创新与金融业转型升级

数字化思维创新与金融业转型升级 课程背景&#xff1a; 很多金融机构存在以下问题&#xff1a; 金融机构的员工不知道需要具备什么样的数字化思维 不清楚数字化思维对金融机构转型升级的重要影响&#xff1f; 不清楚数字化背景下如何进行金融机构转型升级&#xff1f; …

flac格式如何转mp3,3招帮你搞定

flac格式如何转mp3&#xff0c;3招帮你搞定的方法来啦。当你的音频是flac格式是不是很头疼&#xff0c;又不知道怎么转mp3 。然后网上搜索出很多方法又不知道从哪个下手&#xff0c;是不是很疑惑&#xff1f;那今天就来看看小编推荐的方法吧&#xff0c;一定让你眼前一亮&#…

【机会约束、鲁棒优化】机会约束和鲁棒优化研究优化【ccDCOPF】研究(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

汽车制造数字化转型如何做?有哪些可行性案例?

引语&#xff1a;砥砺前行的先行者&#xff0c;为长期主义者带去曙光 国内制造企业亟需加速探索数字化转型之路。但是传统软件服务商提供的PLM、MES等系统已经无法满足企业个性化需求。通过传统软件服务商进行二次开发&#xff0c;成本高、周期长&#xff0c;难以适应迅速变化的…

威胁行为者针对云中的常见漏洞

Palo Alto Networks 已发布其第 42 单元云威胁报告的第 7 卷。该报告调查了 1300 多家组织。它分析了所有主要云服务提供商 (CSP) 的 210000 个云帐户、订阅和项目中的工作负载&#xff0c;为安全领导者和从业者提供了云安全的多方面视图。 云迁移的速度从 2021 年的 3700 亿…

图的存储及基本操作总结(邻接矩阵、邻接表)及C/C++代码实现

文章目录 前言一、邻接矩阵1.概念2.图像示例3. 代码实现注意邻接矩阵的特点 二、邻接表1.概念2.图像示例3.代码实现邻接表的特点 前言 图是一种比较复杂的数据结构&#xff0c;每个结点之间可以有多种关系。 所以&#xff0c;一个图可以呈现出千奇百怪的形式。 对于不同的形式…

java调用webservicer的方法

对于使用 Webservicer的方式&#xff0c;一般采用 Java API调用的方式。Webservicer是一个运行在浏览器中的客户端程序&#xff0c;它可以通过 Webservicer的接口来访问服务器上的服务。 使用 Java调用 Webservicer有两种方式&#xff1a; 下面是一个简单的例子&#xff1a; 2、…

【Vue】学习笔记-初始化脚手架

初始化脚手架 初始化脚手架说明具体步骤脚手架文件结构 初始化脚手架 说明 Vue脚手架是vue官方提供的标准化开发工具&#xff08;开发平台&#xff09;最新版本是4.x文档Vue CLI 具体步骤 如果下载缓慢请配置npm淘宝镜像 npm config set registry http://registry.npm.taoba…

浅谈个人对“孔乙己的长衫“的感受

名人说&#xff1a;往者不可谏&#xff0c;来者犹可追。——《论语微子篇》 创作者&#xff1a;Code_流苏(CSDN) ★温馨提示&#xff1a;以下仅代表个人观点&#xff0c;不代表其它任何人看法。 目录 〇、缘由一、社会对于学历和职业之间的关系认知是怎样的&#xff1f;二、学…

【算法】从x的n次方看递归时间复杂度计算

从x的n次方看递归时间复杂度计算 1.循环 这个问题&#xff0c;最简单的办法是用循环 int pow1(int x,int n) {int result 1;for(int i0;i<n;i){result*x;}return result; }如上算法的时间复杂度为O(N)&#xff0c;但还是不够理想。这时尝试使用递归算法 2.递归1 int po…

51单片机入门

文章目录 一、安装keil5及proteus二、MCS-51单片机结构与原理(一).8051单片机基本组成(二).8051单片机引脚1.电源引脚2.时钟电路引脚3.控制信号引脚4.输入/输出端口 (三) 并行输入/输出端口结构 三、单片机cx51编程基础(一).变量定义(二).数据类型(三).存储类型(四).Cx51语言程…

快手社招Java后端开发岗面试,被问麻了

社招面试是基于你的工作项目来展开问的&#xff0c;比如你项目用了 xxx 技术&#xff0c;那么面试就会追问你项目是怎么用 xxx 技术的&#xff0c;遇到什么难点和挑战&#xff0c;然后再考察一下这个 xxx 技术的原理。 今天就分享一位快手社招面经&#xff0c;岗位是后端开发&…

使用vue.component全局注册组件、props的使用

通过components注册的是私有子组件 例如&#xff1a; 在组件A的 components 节点下&#xff0c;注册了组件F。 则组件F只能用在组件A中;不能被用在组件C中。 注册全局组件 在vue项目的 main.js 入口文件中&#xff0c;通过 Vue.component() 方法&#xff0c;可以注册全局组件…

springboot+vue小区物业管理系统(源码+文档)

风定落花生&#xff0c;歌声逐流水&#xff0c;大家好我是风歌&#xff0c;混迹在java圈的辛苦码农。今天要和大家聊的是一款基于springboot的小区物业管理系统。项目源码以及部署相关请联系风歌&#xff0c;文末附上联系信息 。 &#x1f495;&#x1f495;作者&#xff1a;风…

【2023软考】信息系统监理师与系统集成项目管理工程师哪个更好考?

肯定是系统集成项目管理工程师更好考。 软考信息系统监理师是一项国家级专业职业资格证书&#xff0c;是我国信息技术行业的重要职业资格之一。软考信息系统监理师主要从事信息系统建设项目的监理和管理工作&#xff0c;包括项目前期准备、项目实施阶段和项目验收阶段的监理和…

字符串总结

一、最长公共前缀 1.方法一&#xff1a;横向扫描 class Solution { public:string longestCommonPrefix(vector<string>& strs) {if (!strs.size()) {return "";}string prefix strs[0];int count strs.size();for (int i 1; i < count; i) {prefix…

VS同时调试主程序和子程序工具

VS要想要实现同时调试主程序和子程序&#xff0c;可使用工具 Microsoft Child Process Debugging power Tool 来实现。 我的环境和官方使用说明 环境&#xff1a;VS2019 官方使用说明&#xff1a;Introducing the Child Process Debugging Power Tool - Azure DevOps Blogh…

Shell编程规范与使用

目录 一、Shell脚本概述 1&#xff09;Shell的作用——命令解释器&#xff0c;“翻译官” 2&#xff09;常见shell解释器 3&#xff09;编程语言类型 4&#xff09;Shell脚本 编写脚本代码 Shell脚本的构成 赋予可执行权限 Shell脚本的执行方法 5&#xff09;重定向与…

【学习笔记】unity脚本学习(六)【GUI发展历程、IMGUI控件、Layout自动布局】

目录 unity 界面发展IMGUINGUI其他GUI插件uGUIUI 工具包比较 GUI基础GUI静态变量Unity扩展编辑器屏幕空间的总尺寸Screen.width 和 Screen.height GUI静态函数&#xff08;GUI控件&#xff09;Label图片 Box控件Button与RepeatButtonTextFieldTextAreaPasswordField其他控件 GU…

缓存优化---环境搭建

缓存优化 为什么要使用redis缓存&#xff1f; 问题说明 用户数量多&#xff0c;系统访问大&#xff0c;频繁访问数据库&#xff0c;系统性能下降&#xff0c;用户体验差 环境搭建 maven坐标 在项目中的pom.xml文件中导入spring data redis的maven坐标&#xff1a; <depen…