C/C++冒泡排序4种优化方法

冒泡排序应该是大多数人接触的第一种排序方法,虽然它的时间复杂度为O(n^2),但是它简单易懂,代码复杂度低,所以仍有很大的用武之地。最近在总结排序算法,决定重温下冒泡排序,以及它的优化方法。

冒泡排序的原理以及排序过程参考:冒泡排序三分钟彻底理解冒泡排序

冒泡排序的一些属性:

算法最好时间最坏时间平均时间额外空间稳定性
冒泡O(n)O(n2)O(n2)1稳定

普通冒泡排序:

void myBubleSort1(int * nums, int len) {
	int temp;
	for (int i = 0; i < len; ++i) {
		for (int j = 0; j < len-i-1; ++j) {
			if (nums[j] > nums[j+1]) {
				temp = nums[j];
				nums[j] = nums[j+1];
				nums[j+1] = temp;
			}
		}
	}
}

优化1:使用异或运算交换两个数

在冒泡循环中,我们要交换两个变量的值,通常是使用一个临时变量来辅助交换过程。在C++中可以使用swap()函数。如果交换的值是整数,我们可以省略这个临时变量,使用异或运算符交换两个变量的值:

a和b的值进行交换:

a = a^b;
b = b^a;
a = a^b;

 代码实现:

void myBubleSort2(int * nums, int len) {
	for (int i = 0; i < len; ++i) {
		for (int j = 0; j < len - i - 1; ++j) {
			if (nums[j] > nums[j + 1]) {
				nums[j] = nums[j] ^ nums[j + 1];
				nums[j + 1] = nums[j + 1] ^ nums[j];
				nums[j] = nums[j] ^ nums[j + 1];
			}
		}
	}
}

优化2:设置排序完成的标志

冒泡排序需要有两层循环,无论数组是否排好序,都会完成这两层循环,对于最差的情况,比如[9,8,7,6,5,4],对其进行升序排序,这两层循环必不可少;但是对于[9,1,2,3,4,5]这种情况,第一遍循环结束后,整个数组就已经是升序排列的了,但是普通的冒泡排序还会继续进行循环遍历比较,这就对做了不少无用功。所以需要设置一个排序完成的标志,如果排序已经完成,就没必要再继续循环遍历了,直接跳出循环。

void myBubleSort3(int * nums, int len) {
	bool isSwapped;
	for (int i = 0; i < len; ++i) {
		isSwapped = false;
		for (int j = 0; j < len - i - 1; ++j) {
			if (nums[j] > nums[j + 1]) {
				nums[j] = nums[j] ^ nums[j + 1];
				nums[j + 1] = nums[j + 1] ^ nums[j];
				nums[j] = nums[j] ^ nums[j + 1];
				isSwapped = true;
			}
		}
		if(!isSwapped) break;

	}
}

优化3:跳过无意义的比较

优化一仅仅适用于连片有序而整体无序的数据(例如:1, 2,3 ,4 ,7,6,5)。但是对于前面大部分是无序而后边小半部分有序的数据(1,2,5,7,4,3,6,8,9,10)排序效率也不可观,对于种类型数据,我们可以继续优化。既我们可以记下最后一次交换的位置,后边没有交换,必然是有序的,然后下一次排序从第一个比较到上次记录的位置结束即可。
 

void myBubleSort4(int * nums, int len) {
	bool isSwapped;
	int lastSwap = 0;
	int k = len - 1;
	for (int i = 0; i < len; ++i) {
		isSwapped = false;
		for (int j = 0; j < k; ++j) {
			if (nums[j] > nums[j + 1]) {
				nums[j] = nums[j] ^ nums[j + 1];
				nums[j + 1] = nums[j + 1] ^ nums[j];
				nums[j] = nums[j] ^ nums[j + 1];
				isSwapped = true;
				//lastSwap之后的数都是排好序的
				lastSwap = j;
			}
		}
		if (!isSwapped) break;
		k = lastSwap;
	}
}

优化4

优化3的效率有很大的提升,还有一种优化方法可以继续提高效率。大致思想就是一次排序可以确定两个值,正向扫描找到最大值交换到最后,反向扫描找到最小值交换到最前面。例如:排序数据1,2,3,4,5,6,0

void BubbleSort(int arr[], int len)
{
	int i = 0;
	int j = 0;
	int n = 0;//同时找最大值的最小需要两个下标遍历
	int flag = 0;
	int pos = 0;//用来记录最后一次交换的位置
	int k = len - 1;
	for (i = 0; i < len - 1; i++)//确定排序趟数
	{
		pos = 0;
		flag = 0;
		//正向寻找最大值
		for (j = n; j < k; j++)//确定比较次数
		{
			if (arr[j]>arr[j + 1])
			{
				//交换
				int tmp = arr[j];
				arr[j] = arr[j + 1];
				arr[j + 1] = tmp;
				flag = 1;//加入标记
				pos = j;//交换元素,记录最后一次交换的位置
			}
		}
		if (flag == 0)//如果没有交换过元素,则已经有序,直接结束
		{
			return;
		}
		k = pos;//下一次比较到记录位置即可
		//反向寻找最小值
		for (j = k; j > n; j--)
		{
			int tmp = arr[j];
			arr[j] = arr[j - 1];
			arr[j - 1] = tmp;
			flag = 1;
		}
		n++;
		if (flag == 0)//如果没有交换过元素,则已经有序,直接结束
		{
			return;
		}
	}
}

参考文章:【排序】:冒泡排序以及三种优化

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值