排序算法

本文对学习过的排序算法做简要的总结。如发现错误,欢迎纠正。

本文排序默认为升序。

1. 插入排序(冒泡排序)

插入排序也称为冒泡排序,学过C语言的应该都学习过。程序在下面。

冒泡排序使用了两个for循环:外循环for和内循环for。

原理如下:外循环for在第i次循环时,arr[0]至arr[i-1]已经排序完毕。第i次执行完以后,arr[0]至arr[i]排序完毕。也就是说,外循环for确认第i个元素的位置。

                     内循环for每次执行,都会判断当前的j-1元素是否比j元素大,如果大则交换这两个元素,直到j元素大于等于j-1元素。

#include <stdio.h>

void swap(int *i, int *j)
{
	int temp;
	
	temp = *i;
	*i = *j;
	*j = temp;
}

void sort1(int arr[], int num)
{
	int i, j;
	
	for(i = 1; i < num; i++)
		for(j = i; j > 0 && arr[j-1]>arr[j]; j--)
			swap(&arr[j], &arr[j-1]);

}

void main()
{ 
	int x[10] = {51, 35, -3, 17, 24, -5, 11, 38, 90, 62};
	int i;
		
	sort1(x, 10);

	printf("After sort:\n");
	for(i = 0; i < 10; i++)
		printf("%d ", x[i]);
	printf("\n");		
}

输出结果:

After sort:
-5 -3 11 17 24 35 38 51 62 90

2. 改进的插入排序

冒泡排序时,每次都要交换相邻两个元素的值,这就要用到三个赋值语句。那么是否可以通过什么方法,减少赋值语句。

可以看出,交换相邻两个元素的值只不过是为了让第i个元素插入到正确的位置,那么我们可以考虑在执行内循环for之前,将第i个元素先取出来,然后来查找第i个

元素应该插入的位置,最后将第i个元素插入该位置即可。

程序如下。当内循环for执行完时,j也就是我们需要插入的位置。

#include <stdio.h>

void sort2(int arr[], int num)
{
	int i, j, tmp;
	for(i = 1; i < num ; i++){
		tmp = arr[i];
		for(j = i; j > 0 && arr[j-1]> tmp; j--)
			arr[j] = arr[j-1];
		arr[j] = tmp;    /*插入*/
	}
}


void main()
{
	int i;
	int x[10] = {87, 56, 12, 65, -6, 63, 1, 98, 23, 85};
	
	sort2(x, 10);
	printf("After sort:\n");
	for(i = 0; i < 10; i++)
		printf("%d ", x[i]);
	printf("\n");
	
}
输出结果:

After sort:
-6 1 12 23 56 63 65 85 87 98

3.  快速排序1

   这一节将介绍最简单的快速排序。该排序算法每次以待排序数组中的第一个元素为中值,将所以小于该值的元素放在数组左边,大于该元素放在数组的右边,最后将中值插入正确的位置。然后将中值左侧的子数组作为待排序数组重新调用该算法,同时也将中值右侧的子数组作为待排序数组重新调用该算法,直到最后只有一个或没有元素待排序。如此下去。可见,该算法使用了递归。

   算法如下。
代码16~17为基准条件,当只有一个或没有元素待排序时,递归终止。
代码20-24完成了对左右两个子数组的划分,在执行第i次for循环时,m始终指向小于中值的最后一个元素,也就是说arr[lower+1...m] < 中值,且arr[m+1 ...i-1] >= 中值。
代码24将中指插入了正确的位置。
代码26和27则递归调用算法对左子数组和右子数组进行排序。

#include <stdio.h>

swap(int *a, int*b)
{
	int temp;

	temp = *b;
	*b = *a;
	*a = temp;
}

void qsort1(int arr[], int lower, int upper)
{
	int i, m;
	
	if(lower >= upper)
		return;
  
	m = lower;
	for(i = lower+1; i <= upper; i++){
		if(arr[i] < arr[lower])
			swap(&arr[++m], &arr[i]);
	}
	swap(&arr[m], &arr[lower]);

	qsort1(arr, lower, m-1);
	qsort1(arr, m+1, upper);	
}

void main()
{
	int x[10] = {4, 54, 23, 12, 87, 85, 63, 31, 65, 43};
	int i;

	qsort1(x, 0, 9);
	
	for(i = 0; i < 10; i++)
		printf("%d ", x[i]);
	printf("\n");
}

结果如下:

4 12 23 31 43 54 63 65 85 87 

4. 快速排序2

现在假设对n个相同元素组成的数组进行排序。对于这种排序,插入排序的运行时间为O(n),因为每个元素的移动距离都为0。
而快速排序的性能非常差,N-1次划分中每次划分都需要O(n)时间来去掉一个元素,所以总的运行时间为O(n)。因此对快速排序1进行修改,使用双向划分来解决该问题。
算法如下面所示。
代码16~17为基准条件,当只有一个或没有元素带排序时,递归终止。
代码23为最外层while循环,该循环终止时表示排序已经完成。
代码24~25从左往右遍历数组,直到发现大于中值t的元素,因此该do...while退出时,i指向大于中值的元素。
代码26~27从右往左遍历数组,直到发现小于中值t的元素,因此该do...while退出时,j指向小于于中值的元素。
代码29到30判断是否该次排序是否结束。
代码31交换两个元素。交换以后,i指向小于中指的元素,而j指向小于中指的元素。
代码33当最外面的while(1)退出时,从左往右看,j指向最后一个小于中值的元素,因此对该元素和中指进行交换。
代码26和27则递归调用算法对左子数组和右子数组进行排序。

#include <stdio.h>

void swap(int *a, int*b)
{
	int tmp;
	
	tmp = *b;
	*b = *a;
	*a = tmp;
}

void qsort3(int arr[], int lower, int upper)
{
	int t, i, j;

	if(lower >= upper)
		return;
	
	t = arr[lower];
	i = lower;
	j = upper+1;
	
	while(1){
		do i++;
			while(i <= upper && arr[i] < t);
		do j--;
			while(arr[j] > t);

		if(i > j)
			break;
		swap(&arr[i],&arr[j] );
	}
	swap(&arr[lower],&arr[j] );
	qsort3(arr, lower, j-1);
	qsort3(arr, j+1, upper);
}

void main()
{
	int x[10] = {55, 41, 59, 26, 26, 26, 53, 58, 97, 93};
	int i;
	
	printf("Before qsort:\n");
	for(i = 0; i < 10; i++)
		printf("%d ", x[i]);
	printf("\n");	
	

	qsort3(x, 0, 9);

	printf("After qsort:\n");
	for(i = 0; i < 10; i++)
		printf("%d ", x[i]);
	printf("\n");
} 

结果如下:
Before qsort:
55 41 59 26 26 26 53 58 97 93
After qsort:
26 26 26 41 53 55 58 59 93 97


2012.08.05添加算法1,2。
2012.08.08添加算法3。
2012.08.14添加算法4。


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值