天天看点

最容易理解的快速排序方法和程序

        希尔排序相当于直接插入排序的升级,它们同属于插入排序类,堆排序相当于简单选择排序的升级,它们同属于选择排序类。而快速排序其实就是我们前面认为最慢的冒泡排序的升级,它们都属于交换排序类。即它也是通过不断的比较和移动交换来实现排序的,只不过它的实现,增大了记录的比较和移动的距离,将关键字较大的记录从前面直接移动到后面,关键字较小的记录从后面直接移动到前面,从而减少了总的比较次数和移动交换次数。

        快速排序(Quick Sort)的基本思想是:通过一趟排序将待排记录分割成独立的两部分,其中一部分记录的关键字均比另一部分记录的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序的目的。

        以前总觉得快速排序挺晦涩的,大学的时候考试还是得死记硬背呢!今天看到了伍迷老师对各种排序算法的讲解,觉得思路清晰,十分易懂,以后应该不会忘记的干干净净了。放在这里为学习备份~~

下面看这个思想十分容易接受的程序:
#include <stdio.h>

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

/* 交换a中子表的记录,使枢轴记录到位,并返回其所在位置 */
/* 此时在它之前(后)的记录均不大(小)于它。 */
int partition(int a[],int low,int high)
{
	int pivotKey;
	pivotKey = a[low];//用子表的第一个记录作枢轴记录

	while(low < high)// 从表的两端交替地向中间扫描 
	{
		while(low < high && a[high] >= pivotKey)
			high--;
		swap(&a[low],&a[high]);//将比枢轴记录小的记录交换到低端
		while(low < high && a[low] <= pivotKey)
			low++;
		swap(&a[low],&a[high]);//将比枢轴记录大的记录交换到高端
	}
	return low;//返回枢轴所在位置
}

void QSort(int a[],int low,int high)
{
	int pivot;
	if(low < high)
	{
		pivot = partition(a,low,high);//将a数组一分为二,且返回枢值的位置
		QSort(a,low,pivot-1);//对低子表递归
		QSort(a,pivot+1,high);//对高子表递归
	}
}

//快速排序
void QuickSort(int a[],int n)
{
	QSort(a,0,n-1);//由于需要递归调用,因此我们外封装了一个函数
}

void main()
{
	int i;
	int a[8] = {5,4,1,3,9,5,8,2};
	QuickSort(a,8);
	for(i=0;i<8;i++)
	{
		printf("%d ",a[i]);
	}
	printf("\n");
}
           

因为在现实中,待排序的系列极有可能是基本有序的,此时,总是固定选取第一个关键字(其实无论是固定选取哪一个位置的关键字)作为首个枢轴就变成了极为不合理的作法。

改进,于是就有了三数取中(median-of-three)法。即取三个关键字先进行排序,将中间数作为枢轴,一般是取左端、右端和中间三个数,也可以随机选取。这样至少这个中间数一定不会是最小或者最大的数,从概率来说,取三个数均为最小或最大数的可能性是微乎其微的,因此中间数位于较为中间的值的可能性就大大提高了。由于整个序列是无序状态,随机选取三个数和从左中右端取三个数其实是一回事,而且随机数生成器本身还会带来时间上的开销,因此随机生成不予考虑。

我们来看看取左端、右端和中间三个数的实现代码,在Partition函数代码的第3行与第4行之间增加这样一段代码。

int pivotkey;

 int m = low + (high - low)  /* 计算数组中间的元素的下标 */  
 if (a[low]>a[high])   
  swap(&a[low],&a[high]);    /* 交换左端与右端数据,保证左端较小 */
 if (a[m]>a[high])
  swap(&a[high],&a[m]);    /* 交换中间与右端数据,保证中间较小 */
 if (a[m]>a[low])
  swap(&a[m],&a[low]);    /* 交换中间与左端数据,保证左端较小 */
 
 /* 此时L.r[low]已经为整个序列左中右三个关键字的中间值。*/
pivotkey=L->r[low];    /* 用子表的第一个记录作枢轴记录 */
           

继续阅读