排序算法见解(2)

news2024/9/20 16:58:45

1.快速排序

1.1基本思想:

快速排序是通过一趟排序将待排序的数据分割成独立的两部分,其中一部分的所有数据都比另一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。

1.2基本思路:

(1)选择基准:从待排序的序列中选取一个元素作为基准,这个元素可以是序列的第一个、最后一个、中间的一个或者通过某种方式计算得到的值等。

(2)分区:重新排列序列,所有比基准值小的元素摆放在基准前面,所有比基准值大的元素摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于序列的中间位置。这个称为分区操作。

(3)递归排序子序列:递归地将小于基准值元素的子序列和大于基准值元素的子序列排序。

1.3图解过程:

1.3.1hoare版本:

(左边找大,右边找小(先从右边开始找),找到后交换两个位置的值,直到 left=right ,再把 tmp 位置的值与 left 位置的值交换)

动图展示:

这里 tmp 值从右边选取,所以从左先开始找,效果其实一样的。

1.3.2挖坑法:

(1)选 left 做 tmp(tmp 是单趟排序后能排到最后该待的位置的数据,left 同时也作为第一个坑)
(2)right 开始找,right 遇到比 tmp 大或相等的就往左边走,遇到比 tmp 小的就停下,然后将right 赋值给 left(left = right)(填 left 坑),right 则成为了新的坑,进行第三步。(若 right 超过了 left,直接进行第四步)
(3)left 开始找,left 遇到比 tmp 小或相等的就往右边走,遇到比 tmp 大的就停下,然后将 left 赋值给 right(right = left)(填 right 坑),left 则成为了新的坑,进行第二步。(若 left 超过了right,直接进行第四步)
(4)将 tmp 赋值给 left。

动图展示:

这个从右开始取 tmp 值,所以从左先开始找,效果一样。

1.3.3前后指针:

具体思路:先选定左边第一个为 tmp ,同时设定 left 位置为 prev,prev 的后一个位置为 cur,从a[cur] 开始和 tmp 比较,如果 a[cur] 比 tmp 小,就先将 prev++,再让 a[cur] 和 a[prev] 交换,然后 cur++,如果 a[cur] 比 tmp大,那么就不改变 prev,也不用交换,只对 cur++,以此类推,直到cur>right 就结束,最后将 a[prev] 和 tmp 交换就完成了一趟快排。

动图展示:

1.4代码实现:

1.4.1hoare版本:
// 快速排序hoare版本
int PartSort1(int* arr, int left, int right)
{
	//优化1:三数取中
	int mid = chose_middle(arr, left, right);
	Swap(&arr[left], &arr[mid]);//交换两个位置值的函数
	int key = left;
	int begin = left;
	int end = right;
	while (begin < end)
	{
		//右边找小
		while (begin < end && arr[end] >= arr[key])
		{
			end--;
		}
		//左边找大
		while (begin < end && arr[begin] <= arr[key])
		{
			begin++;
		}
		Swap(&arr[begin], &arr[end]);//交换两个位置值的函数
	}
	Swap(&arr[key], &arr[begin]);//交换两个位置值的函数
	return begin;
}


void Quick_sort(int* arr, int left, int right)
{
	// 快速排序hoare版本
	//int key = PartSort1(arr, left, right);
	Quick_sort(arr, left, key - 1);
	Quick_sort(arr, key + 1, right);
}
1.4.2挖坑法:
// 快速排序挖坑法
int PartSort2(int* arr, int left, int right)
{
	//优化1:三数取中
	//int mid = chose_middle(arr, left, right);
	//Swap(&arr[left], &arr[mid]);
	int pit = left;
	int key = arr[left];
	int begin = left;
	int end = right;
	while (begin < end)
	{
		while (arr[end] > key && begin < end)
		{
			end--;
		}
		Swap(&arr[end], &arr[pit]);
		pit = end;
		while (arr[begin] < key && begin < end)
		{
			begin++;
		}
		Swap(&arr[begin], &arr[pit]);
		pit = begin;
		Print_arr(arr, 9);
		printf("\n");
	}
	Swap(&arr[pit], &key);
	return begin;
}


void Quick_sort(int* arr, int left, int right)
{
	// 快速排序挖坑法
	int key = PartSort2(arr, left, right);
	Quick_sort(arr, left, key - 1);
	Quick_sort(arr, key + 1, right);
}
1.4.3前后指针:
// 快速排序前后指针法
int PartSort3(int* arr, int left, int right)
{
	//优化1:三数取中
	int mid = chose_middle(arr, left, right);
	Swap(&arr[left], &arr[mid]);
	int key = left;
	int prev = left;
	int cur = prev + 1;
	while (cur <= right)
	{
		if (arr[cur] < arr[key] && ++prev != cur)
			Swap(&arr[cur], &arr[prev]);
		cur++;
	}
	Swap(&arr[prev], &arr[key]);
	return prev;
}


void Quick_sort(int* arr, int left, int right)
{
	// 快速排序前后指针法
	int key = PartSort3(arr, left, right);
	Quick_sort(arr, left, key - 1);
	Quick_sort(arr, key + 1, right);
}

2.计数排序

2.1基本思想:

计数排序在于将输入的数据值转化为键存储在额外开辟的数组空间中。用新数组对应下标位置的值来表示该数在原数组出现的次数,计数排序要求输入的数据必须是有确定范围的整数。

2.2基本思路:

 (1)找出待排序数组中的最大值和最小值:这是为了确定计数数组(或称为桶)的大小和范围。计数数组的大小通常是最大值与最小值之差加1(如果包含负数,则需要调整以包含所有可能的值)。

 (2)创建计数数组并初始化:根据步骤1确定的范围,创建一个新的数组,用于记录每个元素在原数组中出现的次数。这个数组的所有元素初始化为0。

(3)统计每个元素的出现次数:遍历原数组,对于每个元素,将其值作为计数数组的索引(如果元素是负数或范围超出常规,则需要进行适当的调整,如加上一个偏移量),并将该索引位置上的值加1。这样,计数数组的每个位置就记录了原数组中对应元素的出现次数。

(4)对计数数组进行累加(可选,但有助于后续排序):从计数数组的第一个元素开始,向后遍历,将每个元素的值更新为当前元素与前一个元素之和。这一步是为了确定每个元素在排序后数组中的位置。例如,如果某个元素在原数组中出现了n次,并且它是第k个不同的元素,那么它在排序后的数组中应该占据从第k个位置开始的n个连续位置

(5)根据计数数组重构原数组:这是排序的最后一步。从后向前遍历原数组(或者从后向前遍历计数数组,同时遍历一个指针指向排序后数组的末尾),对于原数组中的每个元素,找到它在计数数组中的索引,并根据计数数组的值(或者累加后的值)确定它在排序后数组中的位置,然后将该元素放到排序后数组的相应位置上,并减少计数数组中对应索引的值(如果进行了累加)。这个过程可能需要从后向前遍历原数组,以确保稳定性(即相等元素的相对顺序不变)。

(6)处理负数或特殊范围:如果原数组中包含负数或元素范围超出了常规整数范围,可能需要在步骤1和步骤2中进行适当的调整,如加上一个偏移量,以确保所有元素都能被正确地映射到计数数组的索引上。

2.3图解过程:

  

动图展示:

  

2.4代码实现:

// 计数排序
void CountSort(int* arr, int n)
{
	int min = arr[0];
    int max = arr[0];
	for (int i = 1; i < n; i++)
	{
		if (arr[i] < min)
		{
			min = arr[i];
		}
		if (arr[i] > max)
		{
			max = arr[i];
		}
	}

	//创建一个数组来计算(相对)下标出现的次数
	int count = max - min + 1;
	//calloc会自动初始化数组为0
	//int* tmp = (int*)calloc(count,sizeof(int));
	//if (tmp == NULL)
	//{
	//	perror("calloc error");
	//	return;
	//}

	//使用malloc时需要初始化数组
	int* tmp = (int*)malloc(count * sizeof(int));
	if (tmp == NULL)
	{
		perror("malloc error");
		return;
	}
	for (int i = 0; i < count; i++)
	{
		tmp[i] = 0;
	}

	//把原数组映射到开辟的数组中
	for (int i = 0; i < n; i++)
	{
		tmp[arr[i] - min]++;
	}

	//排序
	int j = 0;
	for (int i = 0; i < count; i++)
	{
		while (tmp[i]--)
		{
			arr[j] = i + min;
			j++;
		}
	}
	free(tmp);
	tmp = NULL;
}

3.归并排序

3.1基本思想:

归并排序基本思想是将一个数组分成两半,对每半部分递归地应用归并排序,然后将排序好的两半合并在一起。这个过程一直递归进行,直到数组被分割成只包含一个元素的子数组,这些子数组自然就是排序好的。然后,算法开始合并这些子数组,直到合并成一个完整的排序好的数组。

3.2基本思路:

(1)分解:将数组分解成两个较小的子数组,直到子数组的大小为1。

(2)递归进行排序并合并:递归地对子数组进行归并排序,并将已排序的子数组合并成一个大的有序数组,直到合并为1个完整的数组

3.3图解过程:

3.4代码实现:

void _Merge_sort(int* arr, int* arr1, int left, int right)
{
	if (left >= right)
	{
		return;
	}
	//划分区间
	int key = (left + right) / 2;
	//[left,key],[key+1,right]
	_Merge_sort(arr, arr1, left, key);
	_Merge_sort(arr, arr1, key + 1, right);

	//归并
	int left1 = left;
	int right1 = key;
	int left2 = key + 1;
	int right2 = right;
	int i = left;
	while (left1 <= right1 && left2 <= right2)
	{
		if (arr[left1] < arr[left2])
		{
			arr1[i] = arr[left1];
			i++;
			left1++;
		}
		else
		{
			arr1[i] = arr[left2];
			i++;
			left2++;
		}
	}
	while (left1 <= right1)
	{
		arr1[i] = arr[left1];
		i++;
		left1++;
	}
	while (left2 <= right2)
	{
		arr1[i] = arr[left2];
		i++;
		left2++;
	}
	//交换完一次拷贝一次
	memcpy(arr + left, arr1 + left, (right - left + 1) * sizeof(int));
}

//归并排序(递归)
void Merge_sort(int* arr, int n)
{
	int* arr1 = (int*)malloc(sizeof(int) * n);
	if (arr1==NULL)
	{
		perror("malloc error");
		return;
	}
	_Merge_sort(arr, arr1, 0, n - 1);
	free(arr1);
	arr1 = NULL;
}

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

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

相关文章

如何构建Java SpringBoot文献检索系统:高效管理学术资料,掌握数据库核心技术

✍✍计算机编程指导师 ⭐⭐个人介绍&#xff1a;自己非常喜欢研究技术问题&#xff01;专业做Java、Python、微信小程序、安卓、大数据、爬虫、Golang、大屏等实战项目。 ⛽⛽实战项目&#xff1a;有源码或者技术上的问题欢迎在评论区一起讨论交流&#xff01; ⚡⚡ Java实战 |…

搭上“跨境电商”便车,中国家居在海外越杀越勇

文&#xff5c;新熔财经 作者&#xff5c;楷楷 从去年至今&#xff0c;已有多家跨境家居企业谋求IPO&#xff0c;包括近期启动IPO辅导的圣奥科技&#xff1b;今年4月正式向港交所递交招股说明书的傲基科技&#xff1b;去年11月启动A股IPO的跨境家居品牌杰西亚&#xff0c;还有…

vulhub xxe靶机通关教程

首先我们进行端口扫描 扫出来端口之后去尝试得出地址为183 发现robots.txt文件 由此我们就得到了两个目录 我们先进入xxe目录&#xff0c;并开始登录抓包 并进入重放器在xml里面构造语句 <!DOCTYPE r [ <!ELEMENT r ANY > <!ENTITY admin SYSTEM "php://filt…

【在Linux世界中追寻伟大的One Piece】传输层协议UDP

目录 1 -> 传输层 2 -> 端口号 2.1 -> 端口号范围划分 2.2 -> 知名端口号 3 -> UDP协议 3.1 -> UDP协议端格式 3.2 -> UDP的特点 3.2.1 -> 面向数据报 3.3 -> UDP的缓冲区 3.4 -> UDP使用注意事项 3.5 -> 基于UDP的应用层协议 1 -…

Vue实战:轻松掌握输入框@功能实现技巧

成员列表 创建 实现成员列表的方式比较简单&#xff0c;其实就是一个列表&#xff0c;一个简单的v-for循环就可以搞定&#xff0c;点击时将当前选择的成员项回调给父组件。 新增一个AtPop.vue文件&#xff1a; <template> <div class"at-pop-index">…

DIFFUSION 系列笔记| Latent Diffusion Model、Stable Diffusion基础概念、数学原理、代码分析、案例展示

目录 Latent Diffusion Model LDM 主要思想 LDM使用示例 LDM Pipeline LDM 中的 UNET 准备时间步 time steps 预处理阶段 pre-process 下采样过程 down sampling 中间处理 mid processing 上采样 upsampling 后处理 post-process LDM Super Resolution Pipeline…

海康视觉二次开发学习笔记8-从回调函数获取结果

回调函数使用方法 通常在方案或流程执行完成后,就可以获取到流程运行的结果.运行一次流程后,我们就可以获取到流程的渲染结果以及流程的数据结果.那么使用通讯或硬件进行外部触发时,如何获取结果呢? 这种时候就要用到回调函数. 1. 注释原获取结果代码 2. 注册回调函数 在构…

【文献及模型、制图分享】数字技术力量下传统村落景观修复演进的特征与机制研究——以岳阳市张谷英村为例(GIS空间分析、点云提取)

文献介绍 景观修复作为弘扬中华优秀传统文化的重要方式&#xff0c;如何在乡村数字化新时代背景下&#xff0c;把握传统村落景观修复的数字赋能&#xff0c;已成为推动中华优秀传统文化创造性转化与创新性发展亟需解决的科学问题。运用深度访谈、GIS空间分析、点云数据提取等方…

html+css+js网页设计 婚庆类型12个页面

htmlcssjs网页设计 婚庆类型12个页面 网页作品代码简单&#xff0c;可使用任意HTML编辑软件&#xff08;如&#xff1a;Dreamweaver、HBuilder、Vscode 、Sublime 、Webstorm、Text 、Notepad 等任意html编辑软件进行运行及修改编辑等操作&#xff09;。 获取源码 1&#xf…

FreeRTOS 低功耗模式

正如STM32的裸机编程一样&#xff0c;FreeRTOS通用提供了低功耗模式。 前面说的很明白&#xff0c;FreeRTOS的低功耗模式实际上还是基于STM32的低功耗模式指令进入睡眠模式来实现的&#xff0c;并且只要中断来临&#xff0c;就会退出低功耗&#xff0c;FreeRTOS的系统时钟是最底…

猫头虎分享:Python库 Statsmodels 的简介、安装、用法详解入门教程

猫头虎分享&#xff1a;Python库 Statsmodels 的简介、安装、用法详解入门教程 &#x1f42f; 引言 &#x1f3af; 今天猫头虎带您 深入探讨 Statsmodels 这个在数据分析和统计建模领域非常重要的Python库。最近有粉丝在评论区问道&#xff1a;“猫哥&#xff0c;如何使用 St…

USB端点

USB端点 各端点使用循环冗余校验&#xff08;CRC&#xff09;来检测传输中发生的错误。 根据 USB 规范&#xff0c;设备端点是 USB 设备中一个独特的可寻址部分&#xff0c;它作为主机和设备间通信流的信息源或库。 USB 枚举和配置一节介绍了设备向默认地址做出响应的步骤。 枚…

能进大厂的自动化测试面试题

前言 每次到金九银十都避免不了要聊一聊面试题了&#xff0c;如今九月已经是中下旬了&#xff0c;马上就要到十月份了&#xff0c;还在投简历找工作的小伙伴可以看看我这几天发的文章&#xff0c;最近发的都是面试题&#xff0c;如果需要笔者教一下大家怎么写简历的小伙伴可以…

光伏高压并网升压箱变

在当今能源领域的变革浪潮中&#xff0c;光伏能源以其清洁、可再生的显著优势&#xff0c;成为了备受瞩目的焦点。而光伏高压并网升压箱变&#xff0c;则是实现光伏电能顺利接入高压电网的核心设备。 光伏高压并网升压箱变宛如光伏能源系统中的一位“大力士”&#xff0c;承担着…

一张图浏览CSS Functions

点我CSS Functions思维导图下载 函数文章链接示例属性函数var attr env介绍访问文章示例地址

银河麒麟编译opencv库并配置qt环境

1.opencv下载版本:opencv4.5.5,qt安装的是qt5.12.11,系统版本: 2.首先应该安装cmake工具: 下载地址:https://cmake.org/download/ 安装步骤: 1)解压; 2)进入解压后的文件夹cd cmake-3.30.2 3)./bootstrap 4)sudo make 5)sudo make install 3.下载opencv,下…

实现 GridLayoutManger 和 StaggeredGridLayoutManager 混排的工具类

序言 最近项目中要实现瀑布流的混排&#xff0c;于是写了一些工具类来实现。使用了这个工具类&#xff0c;可以处理混排&#xff0c;可以处理间距。都集成在一个接口中。 最后效果类似这样。 工具类 GridItemUI 下面的这个类是用来实现在GridLayouManger中混排的。 packag…

硬件-PCB-正片(常用默认)和负片

文章目录 问题&#xff1a;什么是PCB的正片和负片&#xff1f;1.正片设计默认是无铜的&#xff08;常用&#xff09;2.负片设计是默认有铜的3.网友评论道友&#xff1a;我们的对手从来不是别人&#xff0c;而是自己。如果有什么必须战胜&#xff0c;那就是过去的自己。战胜自己…

Nginx 部署前端 Vue 项目全攻略

一、前期准备工作 要将 Vue 项目部署到 Nginx &#xff0c;需要做好以下准备工作&#xff1a; Nginx 的安装&#xff1a; 对于 Centos 系统&#xff0c;可以通过 yum install -y nginx 命令来安装 Nginx 。 对于 Windows 系统&#xff0c;需要在 Nginx 官网下载相应的安装包并…

数据迁移新技能,MongoDB轻松同步至ClickHouse

在当今数据驱动的世界中&#xff0c;企业的成功依赖于对数据的高效管理和精准分析。数据迁移是实现这些目标的关键环节&#xff0c;而选择合适的工具可以让这项工作变得更加轻松和高效。ETLCloud 是一款创新的 ETL&#xff08;提取、转换、加载&#xff09;工具&#xff0c;它提…