数据结构之交换排序

目录

交换排序

冒泡排序

冒泡排序的时间复杂度

快速排序

快速排序单趟排序的时间复杂度

快速排序的时间复杂度


交换排序

在日常生活中交换排序的使用场景是很多的,比如在学校做早操,老师通常会让学生按大小个排队,如果此时来了一个新学生,要让他进入队伍,此时就要让他先与队列的第一个学生进行比较,发生交换,最终将他排到合适的队列位置,这就是一个简单的交换排序的使用场景。在数据结构中,我们怎样实现交换排序呢

 交换排序分为冒泡排序快速排序(重点),下来就让我们一起研究这两个排序。

冒泡排序

冒泡排序的思想:我们将冒泡排序分成了多个单趟排序,每趟排序找出最大的元素,并且将最大的元素放置于数组的末尾。

冒泡排序的单趟排序代码:

for (int i = 0; i < size -j-1; i++)
		{
			//比较两个元素,因为是排升序,所以如果第一个元素比第二个元素要大,就要发生交换
			if (a[i] > a[i + 1])
			{
				Swap(&a[i], &a[i + 1]);
				flag = 0;
			}
		}

冒泡排序整体代码:

void BubbleSort(int* a, int size)
{
	//整体的排序
	for (int j = 0; j < size - 1; j++)
	{
		//定义一个标志变量,为循环结束的条件
		int flag = 1;
		//单趟排序:先让第一个元素与第二个元素进行比较大小,因为是排升序,所以如果第一个元素比第二个元素大,要发生交换,依此步骤,完成一次单趟排序
		for (int i = 0; i < size -j-1; i++)
		{
			//比较两个元素,因为是排升序,所以如果第一个元素比第二个元素要大,就要发生交换
			if (a[i] > a[i + 1])
			{
				Swap(&a[i], &a[i + 1]);
				flag = 0;
			}
		}
		//优化:如果进行了一趟排序之后,没有元素发生交换,就意味着数组已经变得有序,所以就没有必要再去进行下一趟排序了
		if (flag == 1)
		{
			break;
		}
	}
}

int main()
{
	int arr[] = { 1000,999,888,777,666,555,444,333,222,111 };
	BubbleSort(arr, sizeof(arr) / sizeof(int));
	for (int i = 0; i < sizeof(arr) / sizeof(int); i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

 运行截图如下:

 注意:我们里面定义了一个flag变量为循环结束的标志,因为我们对冒泡排序做了优化,因为当一个数组已经有序时,我们人眼可以看出来,但是编译器是看不出来的,所以,我们要设置这个变量,为的就是,当我们进行了一趟排序后,如果没有发生元素的交换,证明此时数组已经变得有序了,所以就没有必要再去进行下一趟排序了,应该直接终止排序,即跳出循环。

冒泡排序的时间复杂度

时间复杂度: 最好:O(N)     最坏:O(N^2)

稳定性:稳定

快速排序

快速排序是排序中最牛也是最重要的排序。 本期我们主要讲解快速排序的递归版本

快速排序的思想:先进行单趟排序,单趟排序找出key之后,对key左边的数组和key右边的数组依次进行快速排序,按照递归的思想最终完成排序。

单趟排序的思想:

方法一:hoare版本,这个版本是发明快排的大佬所使用的版本,有些难懂,称之为原始版本。

主要思路:1.设置两个变量left和right,分别表示数组第一个元素和最后一个元素的位置,然后规定key为left或者right的位置。

                  2.从left位置开始,找比key位置上的元素大的元素,从right位置开始,找比key位置上的元素小的元素,这里需要注意:如果选取left为key,应从right位置开始找,如果选取right为key,应该从left位置开始找。

                  3.当找到了对应的元素之后,将两个位置上的元素进行交换,分别让left++,right--然后重复上述2步骤,直到left和right处于同一位置,将此位置上的元素与key位置上的元素进行交换,然后将key挪动到此位置,一趟排序就完成了,此时key位置左边的所有位置的元素都比key位置上元素要小,key位置右边的元素都比key位置上的元素都要大,此时就证明key位置上的元素已经放到了最终的位置,排好了序。

单趟排序图示如下:

单趟排序的代码如下:

int PartSort1(int* a, int left, int right)
{
	int key = left;
	while (left < right)
	{
		while (left<right && a[right]>=a[key])
		{
			right--;
		}
		while (left < right && a[left] <= a[key])
		{
			left++;
		}
		swap(&a[right], &a[left]);
	}
	swap(&a[left], &a[key]);
	key = left;
}

   大家注意这两行代码:

为什么限制条件必须这样写呢?因为我们要避免两种极端情景。

情景1:当数组元素相同时

当数组元素相同时,按照我们以往的算法,从right开始找比key小的,如果比key大就right--,,然后在从left开始找,找比key大的,如果比key小就left++,但是在这种情况下right不可能比key大的,所以right就不可能--,left也不可能比key值小,所以left就不可能++,里面的while循环无法正常进行,所以为了避免这样情况下我们就要,增加判断条件,即a[right]>=a[key]和a[left] <= a[key],比以往多了一个==,这样就会让left++和right--正常进行。

情景2:当数组已经是个有序数组时:

这种情景刚开始都是正常的,但是大家仔细思考下面的两行代码。

当right和left相遇之后,因为最外层的控制条件只会堆内层的循环产生一次限制影响,所以此时的right仍然会再次--,这就会导致right越界。所以我们还必须加上一个限制条件,里面的while循环也必须加上left<right的限制条件,所以改善之后的代码如下:

单趟排序方法二:挖坑法

主要思路:1.将第一个数据存放在临时变量key中,然后第一个位置形成一个坑位。然后从right开始找比key小的元素,找到之后与放置到坑位,然后自己成为坑位。

                  2.从left开始找比key大的元素,找到之后放置到坑位,然后自己形成一个坑位。

                  3.重复1,2,直到left和right相遇。最终将key的值放置在坑位,最红key左边的值都小于key的值,右边的值都大于key的值。

单趟排序第二种方法代码:

int PartSort2(int* a, int left,int right)
{
	int mid = GetMid(a, left, right);
	Swap(&a[left], &a[mid]);
	int key = a[left];
	int pivot = left;
	while (left < right)
	{
		while (left<right && a[right]>=key)
		{
			--right;
		}
		a[pivot] = a[right];
		pivot = right;
		while (left < right && a[left] <= key)
		{
			++left;
		}
		a[pivot] = a[left];
		pivot = left;
	}
	a[left] = key;

}

快速排序整体代码:

void  QuickSort(int* a, int left, int right)
{
	if (left >= right)
		return;
	int key= PartSort1(a, left, right);
	QuickSort(a, left, key - 1);
	QuickSort(a, key + 1, right);
}
int main()
{
	int arr[] = { 100,99,88,77,66,55,44,33,22,11 };
	QuickSort(arr,0,sizeof(arr)/sizeof(int)-1);
	for (int i = 0; i < sizeof(arr) / sizeof(int); i++)
	{
		printf("%d ", arr[i]);
	}
	return 0;
}

 运行截图如下:

快速排序单趟排序的时间复杂度

left和right从数组两端一直到相遇,再与key交换,整个过程刚好每个元素都和key位置的元素比较了一次,所以快速排序单趟排序的时间复杂度为O(N)

快速排序的时间复杂度

最好:O(N*logN)

最坏:O(N^2)

稳定性不稳定。 

综上我们知道,当数组有序时吗,快排的效率非常差劲,为了提高效率,我们发明了一种三数取中法,为一个将有序数组变称无序数组的方法,以提高效率。

具体思路:在进行单趟排序之前,先将left,right和(left+right)/2,位置上的元素分别进行对比,找出出刚好大小处于中间的元素,将次位置设置成mid;然后将mid位置上的元素与left位置上的元素进行交换,就导致left位置上的元素已经是数组中两个元素中间的一个元素,也就意味着进行了这样的一个调整之后,数组一定是无序的。这样就强制性的改变了快排最坏的情况,向最好的情况引导。

 这就保证了,单趟排序之后,key的位置一定不是处于最边上的位置,而是处于中间的位置。

获取中间元素的位置代码:

int GetMid(int* a, int left, int right)
{
	int mid = left + right / 2;
	if (a[left] < a[mid])
	{
		if (a[mid] < a[right])
			return mid;
		else if (a[left] > a[right])
			return left;
		else
			return right;
	}
	else
	{
		if (a[left] < a[right])
			return left;
		else if (a[mid] > a[right])
			return mid;
		else
			return right;
	}
}

 改进后的单趟排序代码:

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

 以上便是交换排序的所有内容,比较重要的还得是快速排序,但是快速排序我们只学习了递归版本,非递归版本在下期为大家讲解。欲知后事如何,且看下期分解。

本期的所有内容到此结束^_^

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

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

相关文章

【电路笔记】-交流电路中的电阻器

交流电路中的电阻器 文章目录 交流电路中的电阻器1、概述2、交流电路中的电阻器示例 13、交流电路中的电阻器示例2 电阻器也可用于交流电源&#xff0c;其中消耗的电压、电流和功率以有效值给出。 1、概述 在之前的文章中&#xff0c;我们研究了电阻器及其连接&#xff0c;并使…

vue2 echarts饼状图,柱状图,折线图,简单封装以及使用

vue2 echarts饼状图&#xff0c;柱状图&#xff0c;折线图&#xff0c;简单封装以及使用 1. 直接上代码&#xff08;复制可直接用&#xff0c;请根据自己的文件修改引用地址&#xff0c;图表只是简单封装&#xff0c;可根据自身功能&#xff0c;进行进一步配置。&#xff09; …

<Linux>(极简关键、省时省力)《Linux操作系统原理分析之Linux文件管理(1)》(25)

《Linux操作系统原理分析之Linux文件管理&#xff08;1&#xff09;》&#xff08;25&#xff09; 8 Linux文件管理8.1 Linux 文件系统概述8.2 EXT2 文件系统8.2.1 EXT2 文件系统的构造8.2.2 EXT2 超级块&#xff08;super block&#xff09;8.2.3 组描述符8.2.4 块位图 8.3 EX…

Linux 进程地址空间

文章目录 进程地址空间进程地址空间结构页表虚拟内存写时拷贝 进程地址空间 进程地址空间难以定义&#xff0c;因为它更像是一个中间件。 程序从磁盘中加载到内存&#xff0c;程序的执行需要硬件资源&#xff0c;所以每个程序启动时会创建至少一条进程&#xff0c;进程作为组…

layui日历插件

layui日历插件: 在已开源的layui日历插件的基础上的改版&#xff08;原版插件地址&#xff1a;https://gitee.com/smalldragen/lay-calender-mark&#xff09;https://gitee.com/tangmaozizi/layui-calendar-plugin.gitjava后台代码并没有把项目完整结构上传上去&#xff0c;因…

敏捷:应对软件定义汽车时代的开发模式变革

随着软件定义汽车典型应用场景的落地&#xff0c;汽车从交通工具转向智能移动终端的趋势愈发明显。几十年前&#xff0c;一台好车的定义主要取决于高性能的底盘操稳与动力系统&#xff1b;几年前&#xff0c;一台好车的定义主要取决于智能化系统与智能交互能否满足终端用户的用…

分类预测 | Matlab实现OOA-SVM鱼鹰算法优化支持向量机的多变量输入数据分类预测

分类预测 | Matlab实现OOA-SVM鱼鹰算法优化支持向量机的多变量输入数据分类预测 目录 分类预测 | Matlab实现OOA-SVM鱼鹰算法优化支持向量机的多变量输入数据分类预测分类效果基本描述程序设计参考资料 分类效果 基本描述 1.Matlab实现OOA-SVM鱼鹰算法优化支持向量机的多变量输…

Python中组合数据类型

1.常见的组合类型有3大类&#xff1a; 集合类型&#xff1a;是一个元素集合&#xff0c;元素之间无序&#xff0c;相同元素在集合中唯一存在。集合&#xff08;set&#xff09;序列类型&#xff1a;是一个元素向量&#xff0c;元素之间存在先后关系&#xff0c;通过序号访问&a…

sklearn随机森林 测试 路面点云分类

一、特征5个坐标 坐标-特征-类别 训练数据 二、模型训练 记录分享给有需要的人&#xff0c;代码质量勿喷 import numpy as np import pandas as pd import joblib#region 1 读取数据 dir D:\\py\\RandomForest\\ filename1 trainRS filename2 .csv path dirfilename1file…

QT 中基于 TCP 的网络通信 (备查)

基础 基于 TCP 的套接字通信需要用到两个类&#xff1a; 1&#xff09;QTcpServer&#xff1a;服务器类&#xff0c;用于监听客户端连接以及和客户端建立连接。 2&#xff09;QTcpSocket&#xff1a;通信的套接字类&#xff0c;客户端、服务器端都需要使用。 这两个套接字通信类…

基于PicGo实现Typora图片自动上传GitHub

文章目录 一. 引言二. 原理三. 配置3.1 GitHub 设置3.2 下载配置 PicGo3.3 配置 Typora3.4 使用 一. 引言 Typora是一款非常好的笔记软件&#xff0c;但是有一个比较不好的地方&#xff1a;默认图片是存放在本地缓存中。这就会导致文件夹一旦被误删或电脑系统重装而忘记备份文件…

6.1810: Operating System Engineering 2023 <Lab4 traps: Traps>

一、本节任务 二、要点&#xff08;Traps and system calls&#xff09; 有三种事件会使 CPU 暂停当前的指令执行&#xff0c;并强制将控制转移到处理该事件的特殊代码中&#xff1a; 系统调用&#xff08;ecall&#xff09;&#xff1b;异常&#xff08;如非法指令&#xff…

VSCode之C++ CUDA入门:reduce的N+1重境界

背景 Reduce是几乎所有多线程技术的基础和关键&#xff0c;同样也是诸如深度学习等领域的核心&#xff0c;简单如卷积运算&#xff0c;复杂如梯度聚合、分布式训练等&#xff0c;了解CUDA实现reduce&#xff0c;以及优化reduce是理解CUDA软硬件连接点的很好切入点。 硬件环境&…

JVM 分析GC日志

GC日志参数 -verbose:gc 输出gc日志信息&#xff0c;默认输出到标准输出 -XX:PrintGC 输出GC日志。类似&#xff1a;-verbose:gc -XX:PrintGCDetails 在发生垃圾回收时打印内存回收详细的日志&#xff0c;并在进程退出时输出当前内存各区域分配情况 -XX:PrintGCTimeStam…

【TiDB理论知识10】TiDB6.0新特性

新特性 Placement Rules in SQL 小表缓存 内存悲观锁 Top SQL TiDB Enterprise Manager 一 Placement Rules in SQL Placement Rules in SQL 之前会遇到的问题 比如 北京的业务需要访问 T2 和 T3表 &#xff0c;但是T3表的数据在纽约 纽约的业务需要问访T4 T5 T6表…

2023 金砖国家职业技能大赛网络安全省赛理论题样题(金砖国家未来技能挑战赛)

2023 金砖国家职业技能大赛网络安全省赛理论题样题&#xff08;金砖国家未来技能挑战赛&#xff09; 一、参加比赛的形式 团队参与&#xff0c;每队2名选手&#xff08;设队长1名&#xff09;。 二、项目项目阶段简介 项目由四个阶段组成&#xff0c;将按顺序完成。向参与者…

Notes数据直接在Excel中统计

大家好&#xff0c;才是真的好。 我希望你看过前面两篇内容《Domino REST API安装和运行》和《Domino REST API安装和运行》&#xff0c;因为今天我们正是使用REST API方式在Excel中查询和统计Notes数据。 不过首先你得知道一个OData协议&#xff0c;全名Open Data Protocol(…

Leetcode1038. 从二叉搜索树到更大和树

Every day a Leetcode 题目来源&#xff1a;1038. 从二叉搜索树到更大和树 解法1&#xff1a;中序遍历 观察示例 1&#xff0c;我们发现了规律&#xff1a; 二叉搜索树的中序遍历是一个单调递增的有序序列。 本题中要求我们将每个节点的值修改为原来的节点值加上所有大于它…

CSS——选择器、PxCook软件、盒子模型

1、选择器 1.1 结构伪类选择器 作用&#xff1a;根据元素的结构关系查找元素。 1.1.1 :nth-child&#xff08;公式&#xff09; 作用&#xff1a;根据元素的结构关系查找多个元素 <!DOCTYPE html> <html lang"en"> <head><meta charset"…