浅谈排序算法(一)之冒泡排序

首先介绍的是,相对来说比较简单的冒泡排序。
冒泡排序,其基本思想是(如果我们是要从小到大排序):用两个循环(暂且称为内循环和外循环吧),内循环中:从第1位开始依次比较相邻的两个数的大小,

如果前一位比后一位大,则交换两个他们的值,一直比较到最后;这样一趟下来的话,那么最大的那个数就在最后那个位置了。一次内循环就可以选出一个数的

正确位置。所以外循环的次数最多就是该排序数列的大小

代码一:

int bubbling_01(int array[], int size)   //基本的冒泡排序
{
	int temp;
	//一次内循环就可以选出一个数的正确位置,那么一个大小size的数列要完成排序就要size次,其实不然,只要size-1,	//因为size-1个数都是正确的      那么剩下的一个就肯定是了,所以可以偷一次懒,
	for(int i = 0; i < size - 1; i++) 
	{
		//内层循环只要完成相邻数间的比较就行了。因为每次内循环后就确定了一个数的正确位置,且把它放在后面,所以比较范围是一个逐渐减小		           //(size - i)的范围。至于还要减1,是因为我们选择的是array[j]和array[j + 1]的方式比较所以j的范围要小1,不然j+1就越界
		for(int j = 0; j < size - i - 1; j++)
		{
			if(array[j] > array[j + 1])   //升序
			{
				temp = array[j];
				array[j] = array[j+1];
				array[j+1] = temp;
			}
		}
	}
	return 0;
}


上面的方法是升序排序,为了让它更灵活,我们可以改一下。

代码二:

typedef int (*compFun)(int a, int b);  //定义一个函数指针

int comp_asc(int a, int b)  //升序
{
	return a - b;
}

int comp_desc(int a, int b)  //降序
{
	return  b - a;
}

int bubbling_02(int array[], int size, compFun comp)  
{
	int temp;
	for(int i = 0; i < size - 1; i++)
	{
		for(int j = 0; j < size - i - 1; j++)
		{
			if(comp(array[j], array[j+1]) > 0) 
			{
				temp = array[j];
				array[j] = array[j+1];
				array[j+1] = temp;
			}
		}
	}
	return 0;
}

int main()
{
	int array[10] = {2,1,33,3,6,35,77,42,11,10};
	compFun comp = comp_asc;      //升序

	bubbling_02(array, 10,comp);

	for(int i = 0; i < 10; i++)
		cout<<array[i]<<",";

	cout<<endl;

	return 0;
}


代码二中,我们定义了两个分别用于降序和升序的函数,还有一个函数指针,通过该函数指针,我们可以在bubbling_02中更灵活的决定降序还是升序操作。

代码二虽然对代码一中的方法做了改进,但只是灵活性方面的,下面我们做一下性能上的该进。

 

代码三:


int bubbling_03(int array[], int size, compFun comp)
{
	int temp;
	bool flag = 0;

	for(int i = 0; i < size - 1; i++)
	{
		flag = 0;
		for(int j = 0; j < size - i - 1; j++)
		{
			if(comp(array[j], array[j+1]) > 0)   
			{
				temp = array[j];
				array[j] = array[j+1];
				array[j+1] = temp;
				flag = 1;
			}
		}

		if(flag ==0 )
			break;
	}
	return 0;


在代码一和二中,在某些情况下,我们会发现当外循环经过i次后,该数列已经排序好了,但是程序却还会继续执行,直至外循环结束。明显,这是很浪费计算机

资源的,所以我们做了修改,从而得到代码三。其主要思想是:如果某次内循环结束后没发生数据交换,则已经排好序了。所以我们通过观察flag变量有无发生

改变来判断是否跳出循环。


代码四:

int bubbling_03(int array[], int size, compFun comp) //鸡尾排序
{
	int temp;
	bool flag = 0;

	for(int i = 0; i < size - 1; i++)
	{
		flag = 0;
		//从头到尾扫描
		for(int j = i; j < size - i - 1; j++)
		{
			if(comp(array[j], array[j+1]) > 0)   			{
				temp = array[j];
				array[j] = array[j+1];
				array[j+1] = temp;
				flag = 1;
			}
		}

		//从尾到头扫描
		for(int k = size - i - 1; k > i; k--)
		{
			if(comp(array[k-1], array[k]) > 0)  
			{
				temp = array[k-1];
				array[k-1] = array[k];
				array[k] = temp;
				flag = 1;
			}
		}
		if(flag == 0)
			break;
		for( int m = 0; m < 10; m++)
			cout<<array[m]<<",";
		cout<<endl;
	}
	
	return 0;
}



代码四所示的方法叫做鸡尾排序,是冒泡的变种。增加了一个内循环。第一个内循环从头扫到尾,得出正确的尾部值,第二个内循环从尾扫到头,得出正确的头部值。并且,还利用了flag来减少不必要的循环。鸡尾酒排序最糟或是平均所花费的次数都是O(n2),但如果序列在一开始已经大部分排序过的话,会接近O(n)。

 

--------本人菜鸟一只,文中如有错误,望大牛指出^_^

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值