【数据结构】排序3——交换排序(冒泡排序、快速排序)


交换排序

【基本思想】
两两比较,如果发生逆序则交换,直到所有记录都排好序为止。

常见的交换排序方法:
冒泡排序T(n)=O(n2)
快速排序T(n)=O( nlog2n)

冒泡排序

——基于简单交换思想

【基本思想】
每趟不断将记录两两比较,并按“前小后大”规则交换。
第一个数和第二个数比较,若第一个数大于第二个数,则交换位置,否则,位置不变;
完成之后第二个数和第三个数比较,若第二个数大于第三个数,则交换位置,否则位置不变;
完成之后第三个数和第四个数比较,……
直到比较到最后一个数。

例:
在这里插入图片描述
21<25,位置不变
25<49,位置不变
49>25,互换位置
……
在这里插入图片描述
在这里插入图片描述

在这里插入图片描述
总结:
n个数据记录,总共需要比较n-1趟。
第m趟需要比较n-m次。

冒泡排序算法

void bubble_sort(SqList &L){
	int m,i,j;
	RedType x;//交换时临时存储
	for(m=1;m<=n-1;m++){//总共需要比较m趟
		for(j=1;j<=n-m;j++){
			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;
		}
	}
}

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

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

void bubble_sort(SqList &L){
	int m,i,j,flag=1;//flag作为是否有交换的标记,有交换flag=1,没交换flag=0
	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;
				x=L.r[j];//交换
				L.r[j]=L.r[j+1];
				L.r[j+1]=x;
		}
	}
}

算法分析

时间复杂度:
平均时间复杂度为O(n2)
最好时间复杂度(正序)是O(n)
最坏时间复杂度(逆序)为O(n2)

空间复杂度:
算法中增加一个辅助空间temp
空间复杂度S(n)=O(1)

稳定性:
冒泡排序是一种稳定的排序方法。

快速排序

——改进的交换排序

pivot:枢轴、中心点

【基本思想】
① 任取一个元素为中心(中间数),通常是取第一个元素(枢轴),中间数可以是第一个数、最后一个数、最中间一个数、任选个数等;
② 所有比它小的元素一律前放,比它大的元素一律后放;(小的从前往后放,大的从后往前放)
③ 形成左右两个子表;
④ 对各子表重新选择中心元素并依此规则调整;(递归思想)
⑤ 直到每个子表的元素只剩一个,就达到整个序列有序。

优点:简单;
缺点:花费空间。

改进:
① 每一趟的子表的形成是采用从两头向中间交替式逼近法;
② 由于每趟中对各子表的操作都相似,可采用递归算法。
在这里插入图片描述

把第一次比较结束后的表以49为中心分成左右两个子表,对各子表重新选择中心元素并依此规则调整,这里左边选择了27作为中心元素,右边选择了76作为中心元素。

在这里插入图片描述

【基本步骤】
① 设下标1号元素作中间数(界点)pivotkey放置到0号下标位置,low指向第一个空,high指向末尾下标为L.length的位置。
② 由于low指向空,所以从high开始比较:
若high<pivotkey,则high的元素放置到low位置,且low的位置往后移一个位置low+1;
若high>pivotkey,则high的元素位置不变,high的位置也不变;
若high=pivotkey,则high的元素位置不变,high的位置也不变。
③ 由于high指向空,所以从low开始比较:
若low>pivotkey,则low的元素放置到high位置,且high的位置往后移一个位置high-1;
若low<pivotkey,则low的元素位置不变,low的位置也不变;
若low=pivotkey,则low的元素位置不变,low的位置也不变。
④ 直到low=high,把界点pivotkey移到low和high的位置,停止这趟比较。
⑤ 把第一次比较结束后的表以界点pivotkey为中心分成左右两个子表。
④ 对各子表重新选择中心元素并依此规则调整。(递归思想)
⑤ 直到每个子表的元素只剩一个,就达到整个序列有序。

改进后的快速排序算法

//主函数
void main(){
	QSort(L,1,L.length);//对顺序表L进行排序,需要排序的数据范围从位置1到位置L.length
}

void Partition(SqList &L,int low,int high){
	L.r[0]=L.r[low];//把中间数放到0号空位置
	pivotkey=L.r[low].key;
	while(low<high){//直到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];//此时low的位置和high的位置是一样的,是一个空位置
	return low;
}//时间复杂度:O(n)

void QSort(SqList &L,int low,int high){//对顺序表L进行排序,需要排序的数据范围从位置low到位置L.length
	if(low<high){
		pivotloc==Partition(L,low,high);
		//将L.r[low]到L.r[high]一分为二,pivotloc为枢轴元素排好序的位置
		QSort(L,low,pivotloc-1);//对低字表递归排序
		QSort(L,pivotloc+1,high);//对高字表递归排序
	}	
}//时间复杂度:O(log^2^n)

算法分析

时间复杂度:
可以证明,平均计算时间是O(nlog2n)。
Qsort( ):O(log2n)
Partition( ):O(n)
实验结果表明:就平均计算时间而言,快速排序是我们所讨论的所有内排序方法中最好的一个。

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

稳定性:
快速排序是一种不稳定的排序方法。
例如: 49, 38, 49*, 20, 97, 76
一次划分后: 20, 38, 49*, 49, 97, 76
49和49*位置互换,所有不稳定

划分元素的选取是影响时间性能的关键。
快速排序不适于对原本有序或基本有序的记录序列进行排序,输入数据次序越乱,所选划分元素值的随机性越好,排序速度越快,快速排序不是自然排序方法


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值