冒泡排序的三种优化

  • Post author:
  • Post category:其他



传统的冒泡排序完全可以满足我们最基本的需求,但是也仅仅是最简单的需求,这种简单的两个for循环不加任何的判断语句的形式注定它只能是一种效率最低的算法。


我们先贴一个传统的实现方式,之后的三个优化全部建立在函数排序所使用的消耗上,这也是我们优化一切算法的根本路径。

void BubbleSort(int* arr,int size)
{
	assert(arr&&size);
	if(size==1)
		return;
	for(int i=0;i<size-1;i++)
	{
		for(int j=0;j<size-1-i;j++)
		{
			if(arr[j]>arr[j+1])
			{
				int tmp=arr[j];
				arr[j]=arr[j+1];
				arr[j+1]=tmp;
			}	
		}
	}
}

这种传统的排序次数是固定的,即给出相同数目的几个数组 不论其元素排列是怎么样,时间复杂度都为O(N^2)。尤其当我们遇到下面这种序列

即: 1,2,3,5,4  我们只需要排一趟就可以了,基于这种情况我们给出了下面这种优化

void BubbleSort_Optimize_1(int* a,int size)
{
	assert(a);
	if(size==1)
		return;

	bool flag=0;//定义标志位标记已经有序或无序
	for(int i=0;i<size-1;i++)
	{
		flag=1;//开始置为1
		for(int j=0;j<size-1-i;j++)
		{
			if(a[j]>a[j+1])
			{
				int tmp=a[j];
				a[j]=a[j+1];
				a[j+1]=tmp;
				flag=0;//交换后对flag置0,表示已经有序
			}
		}
		if(flag)
			break;//如果flag为1则说明排序前已经有序
	}
}

然而这种优化只能做到某一次已经排好序的时候我们直接跳跳出来,基于第一种优化我们得到一种启发:当一个数组接近有序的时候我们往往只需要在某一个小范围内排序即可,我们可以用一个标记来表示这个范围的下限,例如遇到下面的情况

然而我们发现,每次排序前或排序后数组的后面都有一部分已经有序,这时我们只要记下最后一次排下的数组的下标下次排序的时候就可以只排序到此下标位置即可

第二个优化版本

void BubbleSort_Optimize_2(int* a,int size)
{
	assert(a);
	if(size==1)
		return;

	bool flag=0;//定义标志位标记已经有序或无序
	int index=size-1;
	int max_index=0;//每一次我们找到无序区的上界
	for(int i=0;i<size-1;i++)
	{
		flag=1;//开始置为1
		max_index=0;//这里也可以不写
		for(int j=0;j<index;j++)
		{
 			if(a[j]>a[j+1])
			{
				int tmp=a[j];
				a[j]=a[j+1];
				a[j+1]=tmp;
				flag=0;//交换后对flag置0,表示已经有序
				max_index=j;//注意不要在这里直接将index置为j
			}
		}
		if(flag)
			break;//如果flag为1则说明排序前已经有序
		index=max_index;//若排序过则将index置为最后一次交换的坐标,若没有则表示已经有序
	}
}
这里的第三种优化是在前两种优化的基础上借用了类似于

选择排序

的思想(有没有发现选择排序和冒泡排序的代码有点相似),但是我们进行的是正反交替扫描,

正着扫描得到最大值



反着扫描得到最小值(或者颠倒顺序)

,这样做的目的是为了当数组本身已经接近有序或部分有序的时候多余的判断,这样我们每次得到无序区的最大值和最小值只对它们排序就可以了。
void BubbleSort_Optimize_3(int* a,int size)
{
	assert(a);
	if(size==1)
		return;
	
	int max_index=0;//每一次排序我们得到无序区的上界
	int min_index=0;//每一次排序我们得到无序区的下界
	int index=size-1;
	bool flag=0;//定义标志位标记已经有序或无序

	for(int i=0;i<size-1;i++)
	{
		flag=1;//开始置为1
		min_index=0;//这里也可以不写
		for(int j=0;j<index;j++)
		{
			//正序扫描找到最大值
 			if(a[j]>a[j+1])
			{
				int tmp=a[j];
				a[j]=a[j+1];
				a[j+1]=tmp;
				flag=0;//交换后对flag置0,表示已经有序
				max_index=j;//注意不要在这里直接将max_index置为j
			}
		}
		if(flag)
			break;//如果flag为1则说明排序前已经有序
		index=max_index;//若排序过则将index置为最后一次交换的坐标,若没有则表示已经有序

		for(int j=index;j>min_index;j--)
		{
			//逆序扫描找到最小值
 			if(a[j]<a[j-1])
			{
				int tmp=a[j];
				a[j]=a[j-1];
				a[j-1]=tmp;
				flag=0;//交换后对flag置0,表示已经有序
			}
		}
		min_index++;
		if(flag)
			break;//如果flag为1则说明排序前已经有序
	}
}



版权声明:本文为Redmoon955331原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。