【啊哈!算法】之三、交换排序

作者:jofranks 原创作品,转载请标明出处!版权所有,侵权必究!

来源:http://blog.csdn.net/jofranks


交换排序的基本思想是:两两比较待排序的数据,发现两个数据的次序相反则进行交换,直到没有反序的数据为止。

本文交换排序有:冒泡排序,快速排序~!


一、冒泡排序

冒泡排序是一种稳定的排序算法,他的时间复杂度是O(n^2)  当然,当数据基本有序的时候,他可以达到线性复杂度!


OK,发一个冒泡排序的动画演示: 冒泡排序动画演示

他的算法基本思想是:

最多进行n-1趟排序,每趟排序时,从底部向上扫描,一旦发现两个相邻的元素不符合规则,则交换。我们 发现,第一趟排序后,最小值在A[1],第二趟排序后,较小值在A[2],第n-1趟排序完成后,所有 元素完全按 顺序排列。


下面来看一下排序的过程:

待排序数据:53  33  19  53  3  63  82  20  19  39

 


第一趟排序:3  53  33  19  53  19  63  82  20  39

第二趟排序:3  19 53  33  19  53  20  63  82  39

第三趟排序:3  19 19  53  33  20  53  39  63  82

第四趟排序:3  19 19  20  53  33  39  53  63  82

第五趟排序:没有反序数据,排序结束。


void bubble_sort(int *a, int N)
{
	int temp;
	for(int i = N; i > 0; i--)
	{
		for(int j = 0; j < i - 1; j++) 
		{
			if(a[j] > a[j + 1])  //大的放后面
			{
				temp = a[j];
				a[j] = a[j + 1];
				a[j + 1] = temp;
			}

		}
	}
}


ok 我们来看一下改进的冒泡排序,用一个标志:

void bubble_sort1(int *a, int N)
{
	int temp;
	int n;
	for(int i = N - 1; i > 0;)
	{
		n = i;  //记录交换的位置
		i = 0;  //假设本次没有交换
		for(int j = 0; j < n; j++)
		{
			if(a[j] > a[j + 1])
			{
				temp = a[j];
				a[j] = a[j + 1];
				a[j + 1]  = temp;

				i = j + 1;
			}
		}
	}
}



二、鸡尾酒排序

他是冒泡排序的一种改进,与冒泡排序不同的地方就是在排序时以双向在序列中进行排序!

他的时间复杂度是O(n^2)  但是,如果数据基本有序的情况下 复杂度将是线性的!


ok下面来看一下代码:

void Cocktal_sort(int *a, int N)
{
	int temp;
	int M = N - 1;
	for(int i = 0; i < M;)
	{
		for(int j = M; j > i; j--)  //将最小的数据排在前面
		{
			if(a[j] < a[j - 1])
			{
				temp = a[j];
				a[j] = a[j - 1];
				a[j - 1] = temp;
			}
		}
		i++;
		//将最大的数据排在后面
		for(int j = i; j < M; j++)
		{
			if(a[j] > a[j + 1])
			{
				temp = a[j];
				a[j] = a[j + 1];
				a[j + 1] = temp;
			}
		}

		M--;
	}
}




三、快速排序

快速排序是一种不稳定的算法, 他的时间复杂度是O(nlgn)  空间复杂度是O(nlgn)

他是采用分治法!

在A[1..n]中任取一个数据元素作为比较的“基准”(不妨记为X),将数据区划分为左右两个部分:A[1..i-1]和A[i+1..n],且A[1..i-1]≤X≤A[i+1..n](1≤i≤n),当A[1..i-1]和A[i+1..n]非空时,分别对它们进行上述的划分过程,直至所有数据元素均已排序为止。

上面是一趟排序的过程!

ok,来看一个完整的例子!

待排序数据:  67  67  14  52  29   9  90  54  87  71

X=67         i                                    j

扫描j          i                           j 

交换          54  67  14  52  29   9  90  67  87  71

扫描i                                 i    j 

交换          54  67  14  52  29   9  67  90  87  71

j=i,结束                              ij  

第一趟排序后:54  67  14  52  29  9  [67]  90 87  71

第二趟排序后: 9  29  14 52  [54]  67 [67] 71  87  [90]

第三趟排序后:[9]  29  14 52  [54  67  67  71] 87 [90]

第四趟排序后:[9]  14 [29] 52  [54 67  67  71 87  90]

第五趟排序后:[9  14  29 52  54  67  67  71 87  90]


来看一下排序的过程:

① 选定基准X(不妨用A[low])

② j向前扫描,直到A[j]<X,交换A[i]与A[j],i+1。保证了A[low..i-1]≤X

③ i向后扫描,直到A[i]>X,交换A[i]与A[j],j-1。保证了X≤A[j..high]

④ 继续执行②、③,直到i=j。这时,X恰好在A[i]位置上。

⑤ 对序列A[low..i-1]及A[i+1..high]按照上述规律继续划分,直到序列为空。


仔细分析算法,我们发现,在排序中,我们总是用基准X与另一数据交换,因此,一趟排序结束后,X就能确切定位其最终位置。


OK,我们来看一下C递归代码:

//递归版本
void quick_sort(int *a, int left, int right)
{
	int temp = a[left];//主元
	if(left >= right)
		return ;

	int i = left, j = right;
	for(; i != j;)
	{
		//开始移动,分治计算,找出比主元大的和比主元小的!小的放前面,大的放后面
		while(i < j && a[j] >= temp)
			j--;
		if(j >= i)
			a[i] = a[j];

		while(i < j && a[i] <= temp)
			i++;
		if(j >= i)
			a[j] = a[i]; 

	}
	a[i] = temp;
	//递归
	quick_sort(a, left, i - 1);
	quick_sort(a, i + 1, right);
}


OK,现在我们分开,看一下代码,先来看取第一个元素为主元:

//我们选首元素为主元
int pation(int *a, int left, int right)
{
	int i = left;
	int j = right;
	int temp = a[left];  

	for(;i < j ;)
	{
		while(i < j && a[j] >= temp)
			j--;
		if(j >= i)
			a[i] = a[j];
		while(i < j && a[i] <= temp)
			i++;
		if(j >= i)
			a[j] = a[i];
	}
	a[i] = temp;
	return i;
}

void quick_sort1(int *a, int left, int right)
{
	if(left < right)
	{
		int index = pation(a, left, right);
		quick_sort1(a, left, index - 1);
		quick_sort1(a, index + 1, right);
	}
}

下面我们再换一种pation函数的写法,下面演示的主要是pation的不同了,所以我只贴这个函数的代码了!

//以首元素为主元
int pation2(int *a, int left, int right)
{
	int temp = a[left];
	int n;
	int i = left;
	for(int j = i; j <= right; j++)
	{
		if(a[j] < temp)
		{
			i++;
			n = a[j];
			a[j] = a[i];
			a[i] = n;
		}
	}

	n = a[i];
	a[i] = a[left];
	a[left] = n;

	return i;
}

下面再来一个将最后一个元素作为主元的情况:

//以最后一个元素为主元
int pation3(int *a, int left, int right)
{
	int temp = a[right];
	int n ;
	int i = left - 1;
	for(int j = left; j < right; j++)
	{
		if(a[j] <= temp)
		{
			//将小元素和大元素呼唤
			i++;
			n = a[i];
			a[i] = a[j];
			a[j] = n;
		}
	}
	//将尾元素挪到第一个较大的元素的位置,
	//也就是说将尾元素和第一个较大的元素交换
	n = a[i + 1];
	a[i + 1] = a[right];
	a[right] = n;

	return i + 1;
}


ok先到这里,过几天把非递归实现补充上来!





---------2012/8/2

---------jofranks 于南昌




原文地址:https://www.cnblogs.com/java20130723/p/3211441.html