排序算法2:直接选择排序与快速排序

news2024/9/24 1:20:05

目录

1.直接选择排序

1.1直接选择排序的优化

2.快速排序

2.1基准值的置位(Hoare版)

 2.2挖坑法

2.3lomuto前后指针


前言

前面我们进入了排序算的讲解。今天我们将继续学习几种重要的排序思想,好,咱们三连上车开始今天的内容。

1.直接选择排序

在元素的集合中选出最大值(最小值),存放在序列的起始位置,直到全部的待排序位置数据排完

  1. 在元素集合中arr[i]-----arr[n-1]中选择值最大(小)数据
  2. 若他不是这组元素中的最后一个数据,则将他与这组的最后一个元素交换。
  3. 在剩余的arr[i]----arr[n-2](arr[i + 1]-----arr[n-1])的集合中,传重复上面的步骤,直到集合剩余最后一个元素! 

 思路十分的简单,我们按照这样的思想来实现一下代码:

void SelectSort1(int* arr, int sz)
{
	for (int i = 0; i < sz; i++)
	{
		int begin = i;
		int min = begin;
		for (int j = begin + 1; j < sz; j++)
		{
			if (arr[min] > arr[j])
			{
				min = j;
			}
		}
		Swap(&arr[min], &arr[begin]);
	}
}

 

 我们这样就实现了排序的目的,但是大家也不难看出,直接这样的排序跟冒泡排序相差无几,同样的效率低下,想要扩大他的使用场景,就要将他进行进一步的优化。

1.1直接选择排序的优化

原来的直接选择排序在排序时只是将特定范围内的最小值与该范围内的第一个元素进行交换,那如果我们在寻找最小值的同时,也来寻找最大值来与最后面的元素进行交换,这样就可以大幅提高排序的效率了。

按照这个思路我们来实现这个代码:

void SelectSort(int* arr, int sz)
{
	int begin = 0;
	int end = sz - 1;
	while (end > begin)
	{
		int min = begin;
		int max = begin;
		//找特定范围中最小的值和最大值
		for (int j = begin + 1; j <= end; j++)
		{
			if (arr[min] > arr[j])
				min = j;
			if (arr[max] < arr[j])
				max = j;
		}
		//避免max与begin都在同一位置,begin和min进行交换后,max数据变成了最小的数据
		if (max == begin)
		{
			max = min;
		}
		Swap(&arr[min], &arr[begin]);
		Swap(&arr[max], &arr[end]);
		end--;
		begin++;
	}
}
	

直接选择牌序的优化就完成了!! 

2.快速排序

 快熟排序是Hoare于1962年提出的一种二叉树结构的交换排序方法,其基本的思想就是:任取待排序的元素序列中的某个元素作为基准值,按照排序码将待排序的集合分割为两个子序列,左子序列中所有元素都要小于基准值,有序列中的元素都大于基准值,然后在左右子序列种重复该过程,直到左右的元素排列到相应的位置为止。

2.1基准值的置位(Hoare版)

这里我们单独创建一个函数来查找排序区域的基准值位置:

int FindKey(int* arr, int left, int right)
{
	//从左边找比基准值大的数据
	//从右边找基准值较小的数据
	int key = left;
	left++;
	while (left <= right)
	{
		while (left <= right&&arr[left] < arr[key])
		{
			left++;
		}
		while (left <= right && arr[right] > arr[key])
		{
			right--;
		}
		if (left <= right)
		Swap(&arr[right--], &arr[left++]);//在遇到一定区域内的值都相等时,需要让基准值的位置向中间移,这样才能让区域越划越小
	}
		Swap(&arr[key], &arr[right]);
	return right;
}

找到基准值后,我们根据二叉树结构的性质,很容易就能够确定使用递归的思想来实现循环划分左右子区域的操作

void QuickSort(int* arr, int left, int right)
{
	if (left >= right)
		return;
	//left and right----->找基准值mid
	//1.如何找基准值
	int keyi = FindKey(arr, left, right);
	//左子序列
	QuickSort(arr, left, keyi - 1);
	//右子序列
	QuickSort(arr, keyi + 1, right );
}

 所以Hoare版的快速排序我们就完成了,接下来我们来验证一下:

int FindKey(int* arr, int left, int right)
{
	//从左边找比基准值大的数据
	//从右边找基准值较小的数据
	int key = left;
	left++;
	while (left <= right)
	{
		while (left <= right&&arr[left] < arr[key])
		{
			left++;
		}
		while (left <= right && arr[right] > arr[key])
		{
			right--;
		}
		if (left <= right)
		Swap(&arr[right--], &arr[left++]);//在遇到一定区域内的值都相等时,需要让基准值的位置向中间移,这样才能让区域越划越小
	}
		Swap(&arr[key], &arr[right]);
	return right;
}
void QuickSort(int* arr, int left, int right)
{
	if (left >= right)
		return;
	//left and right----->找基准值mid
	//1.如何找基准值
	int keyi = FindKey(arr, left, right);
	//左子序列
	QuickSort(arr, left, keyi - 1);
	//右子序列
	QuickSort(arr, keyi + 1, right );
}

 

快速排序在实际的生活工作场景中运用广泛,这得益于他二叉树的结构性质,使其拥有了与堆排序相近甚至更快的速率。 

 2.2挖坑法

思路:创建左右指针。首先从右向左找比基准值小的数据,找到后立即放入左边的坑位,当前位置变为新的“坑”, 然后从左向右找比基准值大的数据,找到后立即放入右边的坑洞中,当前位置变为新的坑,结束循环后将最开始存储的分界值放入当前的“坑”中,返回当前“坑”的下标(即分界值下标)。

 挖坑法相较于Hoare的方法要简单,我们稍加理解就可以将代码写出来。

我们来看代码:

int _QuickSort(int* a, int left, int right)
{
	int mid = a[left];
	int hole = left;
	int key = a[hole];
	while (left < right)
	{
		while (left < right && a[right] >= key)
		{
			--right;
		}
		a[hole] = a[right];
		hole = right;
		while (left < right && a[left] <= key)
		{
			++left;
		}
		a[hole] = a[left];
		hole = left;
	}
	a[hole] = key;
	return hole;
}

本质上这时一种新的找基准值的方法,虽然相较于Hoare版的代码挖坑法的代码会更加的冗余,但是他的思想和实现方法更易于理解。

int _QuickSort(int* a, int left, int right)
{
	int mid = a[left];
	int hole = left;
	int key = a[hole];
	while (left < right)
	{
		while (left < right && a[right] >= key)
		{
			--right;
		}
		a[hole] = a[right];
		hole = right;
		while (left < right && a[left] <= key)
		{
			++left;
		}
		a[hole] = a[left];
		hole = left;
	}
	a[hole] = key;
	return hole;
}
void QuickSort(int* arr, int left, int right)
{
	if (left >= right)
		return;
	//left and right----->找基准值mid
	//1.如何找基准值
	int keyi = _QuickSort(arr, left, right);
	//左子序列
	QuickSort(arr, left, keyi - 1);
	//右子序列
	QuickSort(arr, keyi + 1, right );
}

 

经过验证,我们的代码就实现了! 

2.3lomuto前后指针

创建前后指针,从左往右找比基准值小的进行交换,使得小的数据都排在基准值的左边。

 

前后指针法相比于前面的挖坑法和Hoare方法都要简单,但是思路着实不好想,这就需要我们积累新的方法了:

  • 定义两个指针 prev和cur;
  •  cur指向的位置的数据跟key的值比较;
  • 若arr[cur] < arr[key],prev向后走一步并和cur交换;
  • 若arr[cur] >= arr[key],cur继续向后
int Doublelomuto(int *arr, int left, int right)
{
	int prev = left, cur = left;
	int key = left;
	while (cur <= right)
	{
		if (arr[cur] < arr[key] && ++prev != cur)
		{
			Swap(&arr[prev], &arr[cur]);
		}
		cur++;
	}
	Swap(&arr[key], &arr[prev]);
	return prev;
}

最后我们来验证一下这个代码:

int Doublelomuto(int *arr, int left, int right)
{
	int prev = left, cur = left;
	int key = left;
	while (cur <= right)
	{
		if (arr[cur] < arr[key] && ++prev != cur)
		{
			Swap(&arr[prev], &arr[cur]);
		}
		cur++;
	}
	Swap(&arr[key], &arr[prev]);
	return prev;
}
void QuickSort(int* arr, int left, int right)
{
	if (left >= right)
		return;
	//left and right----->找基准值mid
	//1.如何找基准值
	int keyi = Doublelomuto(arr, left, right);
	//左子序列
	QuickSort(arr, left, keyi - 1);
	//右子序列
	QuickSort(arr, keyi + 1, right );
}

快速排序的三种版本的递归排序方法就实现完了。

好今天的学习就到这里,我们下期再见,拜拜!! 

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

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

相关文章

Modern C++ 智能指针

Why&#xff1f; 原始指针存在缺陷&#xff0c;不符合现代编程语言的需要。 原始指针的缺陷&#xff1a; 指针指向一片内存&#xff0c;使用者无法得知到底是指向了什么&#xff0c;是数组还是对象&#xff1f;使用完指针是否需要销毁&#xff1f;什么时候销毁&#xff1f;如…

B1.2 AArch64 执行状态下的寄存器

B1.2 AArch64 执行状态下的寄存器 在AArch64的执行状态下,在 EL0 上可见的寄存器如下: (1)、R0-R30 31 个通用寄存器,R0 到 R30。每个都可以做为: 一个 64 位的通用寄存器,命名为 X0 到 X30。 一个 32 位的通用寄存器,命名为 W0 到 W30。 (2)、LR X30 通用寄存器用…

文件的读写

一、IO 标准io &#xff08;输入输出&#xff09;站在计算机角度来确定输入输出&#xff0c;在linux里面io都是对文件操作。 so 动态库函数&#xff08;共享库&#xff09;&#xff0c;&#xff08;公共的&#xff0c;用的很多&#xff09;&#xff0c;在user里面存储。 man手…

Studying-代码随想录训练营day58| 拓扑排序精讲、dijkstra(朴素版)精讲

第58天&#xff0c;拓扑排序和最短路径算法讲解&#xff01;&#xff01;&#x1f4aa;(ง •_•)ง&#x1f4aa;&#xff0c;编程语言&#xff1a;C 目录 拓扑排序精讲 拓扑排序的背景 题目&#xff1a;117. 软件构建 (kamacoder.com) 拓扑排序的思路 模拟过程 有环…

基于K8S配置Jenkins主从节点实例

基于K8S配置Jenkins主从节点实例 1.配置Jenkins主节点1.确认 Jenkins Pod 名称2.进入 Jenkins Pod&#xff1a;3.生成SSH密钥对4.将公钥复制到目标节点&#xff1a; 2.配置Jenkins的node1节点1.安装java2.配置 Jenkins node1节点的 Java 路径1.添加Java环境变量2.生效Java环境变…

如何根据 EcoVadis 审核的评分标准改进企业社会责任表现?

要根据 EcoVadis 审核的评分标准改进企业社会责任表现&#xff0c;可以采取以下步骤&#xff1a; ​深入研究评分标准 详细了解每个主题&#xff08;环境、劳工与人权、商业道德、可持续采购&#xff09;及其子主题的具体要求和关键指标。 进行自我评估 对照评分标准&#xf…

未授权访问漏洞(非重点 上)

1.MongoDB 1.在fofo里搜索 port"27017 2.Memcached 1.用fofa语法 port"11211" 搜索资产 2.使用 telnet 连接 3.Zookeeper 1.在 fofa 中使用 port"2181" 获取资源 2.在 kali 中使用 echo envinc ip 2181 测试是否存在漏洞 4.Elasticsearch 1.在 …

⌈ 传知代码 ⌋ MSA+抑郁症模型总结(二)

&#x1f49b;前情提要&#x1f49b; 本文是传知代码平台中的相关前沿知识与技术的分享~ 接下来我们即将进入一个全新的空间&#xff0c;对技术有一个全新的视角~ 本文所涉及所有资源均在传知代码平台可获取 以下的内容一定会让你对AI 赋能时代有一个颠覆性的认识哦&#x…

Java语言程序设计——篇十一(5)

&#x1f33f;&#x1f33f;&#x1f33f;跟随博主脚步&#xff0c;从这里开始→博主主页&#x1f33f;&#x1f33f;&#x1f33f; 欢迎大家&#xff1a;这里是我的学习笔记、总结知识的地方&#xff0c;喜欢的话请三连&#xff0c;有问题可以私信&#x1f333;&#x1f333;&…

基于arcpro3.0.2的北斗网格生成简介

基于arcpro3.0.2的北斗网格生成简介 采用2000坐标系、可基于行政区范围 软件可生成第一级到第十级北斗网格经纬跨度 等分 约赤道处距离 第一级 6X4度 60 和A~V 660 km 第二级 30X30分 12X8 …

sql注入第一关和第二关

第一关&#xff1a; 输入?id1 正常 输入?id1 报错 .0 输入?id1-- 正常 判断他是字符型注入 闭合方式是: ?id1 and 12 union select 1,2,group_concat(schema_name) from information_schema.schemata-- 联合注入 爆出库&#xff1a;ctfshow,ctftraining,information_…

nvidia系列教程-AGX-Orin系统刷机及备份

目录 前言 一、准备工作 二、AGX Orin 系统刷机步骤 三、AGX Orin 系统备份 总结 前言 NVIDIA AGX Orin 是一款高性能的嵌入式计算平台&#xff0c;专为边缘计算和 AI 应用而设计。为了确保系统的稳定性和适应不同的应用场景&#xff0c;用户可能需要对 AGX Orin 进行系统刷…

SpringBoot集成阿里百炼大模型 原子的学习日记Day01

文章目录 概要下一章SpringBoot集成阿里百炼大模型&#xff08;多轮对话&#xff09; 原子的学习日记Day02 整体架构流程技术名词解释集成步骤1&#xff0c;选择大模型以及获取自己的api-key&#xff08;前面还有一步开通服务就没有展示啦&#xff01;&#xff09;2&#xff0c…

2024.8.05(glibc的安装及MySQL的安全用户角色权限)

一、glibc的安装 1、清空/etc目录下的my.cnf [rootlocalhost ~]# ls -l /etc/my.cnf -rw-r--r--. 1 root root 570 6月 8 2017 /etc/my.cnf [rootlocalhost ~]# rm -rf /etc/my.cnf 2、删除mariadb [rootlocalhost ~]# yum -y remove mariadb [rootlocalhost ~]# find / -na…

wps 最新 2019 专业版 下载安装教程,解锁全部功能,免费领取

文章目录 前言软件介绍软件下载安装步骤激活步骤小福利&#xff08;安卓APP&#xff09;软件介绍软件下载安装步骤 前言 本篇文章主要针对WPS2019专业版的安装下载进行详细讲解&#xff0c;软件已激活&#xff0c;可放心使用&#xff1b;并且可以进行账号登录&#xff0c;进行…

Router路由的使用

目录 一.Vue Router的使用&#xff1a; 二.使用vue-router来实现登录页面与主页面展示效果&#xff1a; 1.创建 index.js &#xff1a; 2.在 main.js 导入创建的路由器&#xff1a; 3.在App.vue声明标签&#xff1a; 三.子路由的使用&#xff1a; 1.添加五个组件 2.配置…

光线追踪(纹理映射)

最近在跟着ray trace in one week来学习光线追踪&#xff08;很多概念茅塞顿开&#xff09;做到一半想着记录一下&#xff08;比较随心&#xff09;上面是之前的效果。ray trace in one week Texture Coordinates for Spheres&#xff08;球体纹理坐标&#xff09; u, v 纹理…

K-means聚类算法原理解析

度量最小距离 对于 K-means 聚类算法而言&#xff0c;找到质心是一项既核心又重要的任务&#xff0c;找到质心才可以划分出距离质心最近样本点。从数学角度来讲就是让簇内样本点到达各自质心的距离总和最小。通过数学定义&#xff0c;我们将“质心”具象化&#xff0c;既然要使…

使用SpringBoot+Vue3开发项目(2)---- 设计文章分类的相关接口及页面

目录 一.所用技术栈&#xff1a; 二.后端开发&#xff1a; 1.文章分类列表渲染&#xff1a; 2.新增文章分类&#xff1a; 3.编辑文章分类&#xff1a; 4.删除文章分类 &#xff1a; 5.完整三层架构后端代码&#xff1a; &#xff08;1&#xff09;Controller层&#xff1a…

学习大数据DAY31 Python基础语法4和基于Python中的MySQL 编程

目录 Python 库 模块 time&datetime 库 连接 MySQL 操作 结构操作 数据增删改操作 数据查询操作 上机练习 7 面向对象 OOP 封装 继承 三层架构---面向对象思想模型层 数据层 业务逻辑显示层 上机练习 8 三层架构开发豆瓣网 关于我对 AI 写代码的看法&#xf…