【数据结构(37)】8.3 交换排序

基本思想

  • 每两个元素之间互相比较,如果发现大小关系相反,则将他们交换过来,直到所有记录都排好序为止。
  • 假设希望是从小到大来排序,然而两个数的位置为 10、9, 这时候就需要交换了。

常见的交换排序方法

  1. 冒泡排序:O(n2)。
  2. 快速排序:O(nlog₂n)。

一、冒泡排序

基本思想

  • 每一趟将两两相邻的元素进行比较,小的放左边,大的放右边(交换数据),按照递增来排序,不满足条件就交换数据,满足则不管。
  • 将某个数排到最终的位置,这一轮叫一趟冒泡排序

在这里插入图片描述

  • 每一趟冒泡排序可以让 1 个元素走到最终位置.
    • 对于 6 个元素要进行 5 趟冒泡排序。
    • n 个元素要进行 n-1 趟冒泡排序

在这里插入图片描述

冒泡排序过程(升序)

初始:21,25,49,25*,16,08 ;n = 6。

  1. 第 1 趟
    • 第 1 趟结束后:21,25,25*,16,08,49
    • 第 1 趟结束之后,49 已经有序了,那么下一趟就不用管它了。

在这里插入图片描述

  1. 第 2 趟
    • 第 2 趟结束后:21,25,16,08,25*,49
    • 继续下一趟,每一趟增加一个有序元素。

在这里插入图片描述

  1. 第 3 趟结果:21,16,08,25,25*,49
  2. 第 4 趟结果:16,08,21,25,25*,49
  3. 第 5 趟结果:08,16,21,25,25*,49

总结

  • n 个元素,总共需要 n-1 趟冒泡排序
  • 第 m 趟需要比较 n-m 次
    • 第 1 趟需要比较 5 次,第 2 趟 4 次,第 3 趟 3 次…… 。每一趟的排序的趟数 + 这趟的比较次数 = 元素个数 n 。

1. 冒泡排序算法

//对顺序表L左冒泡排序
void bubble_sort(SqList &L)
{
		int m,i,j;
		RedType x;//交换临时存储

		for(m = 1;m <= n-1;m++)//总共需要m(n-1)趟冒泡排序
		{
				for(j = 1;j <= n-m;j++)//每一趟需要比较n-m次
				{
						if(L.r[j].key > L.r[j+1].key)//前面的比后面的大,发生逆序
						{
								x = L.r[j];
								L.r[j] = L.r[j+1];
								L.r[j+1] = x;
								//交换元素位置
						}
				}
		}
}

冒泡排序特点

  • 优点:每趟结束时,不仅能挤出一个最大值到最后面位置,还能同时部分理顺其他元素;

如何提高效率?

  • 一旦某一趟比较时不出现记录交换,则说明已经排好序了,就可以结束本算法来提高效率。

冒泡排序算法改进

在这里插入图片描述

当第 5 趟结束的时候,之前和之后的元素都已经有序了,所以后面的第 6、7 趟纯属于原地打转,浪费时间。

  • 未发生交换时,后面几趟可以省略。
//对顺序表L进行改进的冒泡排序
void bubble_sort(SqList &L)
{
		int m,i,j;
		flag = 1;//flag作为是否有交换的标记
		RedType x;
	
		for(m = 1;m <= n-1 && flag == 1;m++)
		{
				flag = 0;
				for(j = 1;j <= m;j++)
				{
						if(L.r[j].key > L.r[j+1].key)//发生逆序
						{
								flag = 1;//发生交换,让flag为1,
								//若本趟没有发生交换,则flag保持为0,让最外层的for循环进不来
								
								x = L.r[j];
								L.r[j] = L.r[j+1];
								L.r[j+1] = x;
								//交换元素
						}
				}
		}
}

2. 冒泡排序算法分析

时间复杂度

最好情况(正序)

  • 比较次数:n-1 次。
  • 移动次数:0 次。

最坏情况(逆序)

  • 比较次数:

在这里插入图片描述

  • 移动次数:

在这里插入图片描述

冒泡排序的算法评价

  • 冒泡排序最好时间复杂度是 O(n)
  • 冒泡排序最坏时间复杂度为 O(n2)
  • 冒泡排序平均时间复杂度为 O(n2)
  • 冒泡排序算法中增加一个辅助空间 temp,辅助空间为 S(n) = O(1)
  • 冒泡排序时稳定的(相同元素排好序后相对位置不变)。

二、快速排序

  • 由冒泡排序改进的交换排序

基本思想

  • 从需要排序的数据当中,任取一个元素(通常取第一个)为中心
  • 将所有比它的元素一律往前放(左边的区域从左往右放),比它的元素一律往后放(右边的区域从右往左放),形成左右两个子表
  • 对各个子表重新选择中心元素并依此规则调整
  • 直到每个子表的元素只剩一个

举个例子

  1. 首先先选择第一个元素为中心点,然后划分左右子表。
    • 从中心元素piv开始往后比较,比piv小的元素在左子表中从左往右放,比piv大的元素在右子表中从右往左放

在这里插入图片描述
在这里插入图片描述

  1. 子表也是用同样的方法排序,选择第一个为中心然后划分左右区。

在这里插入图片描述

  1. 当子表中只剩一个元素的时候,就不需要再划分了,说明已经排好位置了。

在这里插入图片描述
在这里插入图片描述

快速排序分析

  • 基本思想:通过一趟排序,将待排序记录分割成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录进行排序,以达到整个序列有序。
  • 具体实现:选定一个中间数作为参考,所有元素与之比较,小的调到其左边,大的调到其右边。
  • (枢轴)中间数:可以是第一个数、最后一个数、最中间一个数、任选一个数等。

算法步骤

一趟快速排序的具体步骤如下:

  1. 选择待排序表中的第一个记录作为枢轴,将枢轴记录暂时存到 r[0] 的位置上。
    • 另外设置两个指针 low 和 high ,初始时分别指向表的下界和上届(第一趟时,low = 1; high = L.length;)。
  2. 从表的最右测位置依次向左搜索,找到第一个关键字小于枢轴关键字 pivotkey 的记录,将其移动到 low 处。
    • 具体操作:当 low < high 时,将 high 所指记录的关键字大于等于 pivotkey,则向左移动指针 high(执行操作 --high);否则将 high 所指记录与枢轴记录交换。
  3. 然后再从表的最左侧位置,依次向右搜索找到第一个关键字大于 pivotkey 的记录和枢轴记录交换。
    • 具体操作:当 low < high 时,若 low 所指记录的关键字小于等于 pivotkey,则向右移动指针 low(执行操作 ++low);否则将 low 所指记录与枢轴记录交换。
  4. 重复步骤 2、3,直到 low 与 high 相等为止。
    • 此时 low 和 high 的位置即为枢轴在此趟排序中的最终位置,原表被分为两个子表。

在这里插入图片描述

快速排序特点

  1. 每一趟的子表的形成是采用从两头向中间交替式逼近法;
    • 一开始将枢轴元素放在 0 号位置,那么它的位置就空出来了,从后面挑比它小的元素填进来,此时小元素的原来位置就空了,再从前面般比这个移动过去的小的元素大的元素填过去,前挑大后填,后挑小前填,依次类推。
    • 直到 low 和 high 重合了,这个地方就是俺们放中心点的位置。
  2. 由于每趟中对各子表的操作都相似,可采用递归算法

1. 快速排序算法

//对顺序表L左快速排序
void Quick(SqList &L)
{
	Qsort(L,1,L.length);
}

//调用前置初值:low=1;high=L.length
//对顺序L中的子序列L.r[low...high]左快速排序
void Qsort(SqList %L,int low,int high)
{		
	if(low < high) //长度大于1
	{
		pivotloc = partition(L,low,high);
		//将L.r[low...high]一分为二,pivotloc为枢轴元素排好序的位置

		QSort(L,low,pivotloc-1);//对左子表递归排序
		Qsort(L,pivotloc+1,high);//对右子表递归排序
	}
}

//对顺序表L中的子表r[low...high]进行一趟排序,返回中心点位置
int Partition(SqList &L,int low,int high)
{
	L.r[0] = L,r[low];			//用子表中的第一个记录左枢轴记录
	pivotkey = L.r[low].key;	//枢轴记录关键字保存在pivotkey中

	while(low < high)			//从表的两段交替的向中间扫描
	{
			while(low<high && L.r[high].key >= pivotkey)
			{
					--high;
			}
			L.r[low] = L.r[high];	//将比中心点小的元素移到到低端
				
			while(low<high && L.r[low].key <= pivotkey)
			{
					++low;
			}
			L.r[high] = L.r[low];	//将比中心点大的元素移动到高端
	}		

	L.r[low] = L.r[0];	//找到了中心点该存放的位置了,将它存进去
	return low;			//返回中心点存储的位置
}

2. 快速排序算法分析

时间复杂度

  • 可以证明,整个算法的平均时间复杂度: O(nlog₂n)
    • 快速排序的递归算法 Qsort():Q(log₂n)
    • 查找中心点位置算法 Partition():O(n)
  • 就平均计算时间而言,快速排序时我们所讨论的所有内排序方法中最好的一个。

空间复杂度

  • 快速排序不是原地排序
  • 由于程序中使用了递归,需要递归调用栈的支持,而栈的长度取决于递归调用的深度。(即使不使用递归,也需要用用户栈
    • 在平均情况下:需要 O(logn) 的栈空间。
    • 最坏情况下:栈空间可达到 O(n)

稳定性

  • 快速排序是一种不稳定的排序。

在这里插入图片描述

自然性

在这里插入图片描述

  • 由于每次枢轴记录的关键字都是大于其他所有记录的关键字,致使一次划分之后得到的子序列(1)的长度为 0,这时已经退化成为没有改进措施时的冒泡排序。
  • 快速排序不适合对原本就有序或基本有序的记录序列进行排序

总结

  • 划分元素的选取是影响时间性能的关键。
  • 输入数据次序越乱,所选划分元素值的随机性越好,排序速度越快,快速排序不是自然排序方法。
  • 改变划分元素的选取方法,至多只能改变算法平均情况下的世界性能,无法改变最坏情况下的时间性能。
    • 最坏情况下,快速排序的时间复杂度总是 O(n2)
  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Officelite 8.3是一款办公软件,它是微软公司推出的一套专业的办公套件。它包含了Word处理软件、Excel电子表格软件、PowerPoint演示软件等多个实用的工具,可以满足用户在办公、学习和日常生活中的各种需求。 首先,Officelite 8.3的Word处理软件是一款功能强大的文字处理工具,可以帮助用户创建、编辑和格式化各种文档,如报告、信件、简历等。用户可以根据自己的需求进行排版、插入图片和表格,并且还可以进行多种文档样式和主题的选择,使文档更具个性和专业性。 其次,Officelite 8.3的Excel电子表格软件是一种可以进行数据分析和处理的工具。用户可以利用Excel创建各种表格、图表和图形,进行数据的输入、计算和统计分析。它还提供了多种公式和函数,方便用户进行复杂的数据计算和处理,大大提高了工作效率。 此外,Officelite 8.3的PowerPoint演示软件可以帮助用户创建精美的演示文稿。用户可以利用丰富的模板、图形和动画效果,打造出专业而生动的演示文稿,使演讲更加吸引人。同时,用户还可以配备音频和视频文件,使演示更具多媒体效果。 总的来说,Officelite 8.3作为一套专业的办公套件,具有丰富的功能和用户友好的界面。它可以满足用户在办公、学习和生活中的各种需求,提高工作效率,提升文档质量。无论是个人用户还是企业用户,Officelite 8.3都是一个非常值得选择的办公软件。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值