首先,对大多数包含排序应用的程序来说,排序算法的速度并不重要,因为在程序中排序 的工作量并不是很多,或者,与排序相比,程序中其它操作所花费的时间要多得多。
实际上,没有哪一种排序算法永远是最快的,在运行程序的软硬件环境相同的情况下,不同排序算法的速度还与数据的长度、性质以及数据的初始顺序有关。
在笔者的“工具箱”中,有三种算法在不同的情况下都是最快、最有用的,这三种算法分别是快速排序、归并排序和基数排序。
快速排序
快速排序是一种分割处理式的排序算法,它将一个复杂的排序问题分解为若干较容易处理的排序问题,然后逐一解决。在快速排序算法中,首先要从数据集的数据中选择一个数据作为分割值,然后将数据分成以下3个子集:
(1) 将大于分割值的数据移到分割值前面,组成子集1;
(2) 分割值本身为子集2;
(3) 将小于分割值的数据移到分割值后面,组成子集3。
等于分割值的数据可以放在任意一个子集中,这对快速排序算法没有任何影响。由于子集2已经是有序的,所以此后只需对子集1和子集3进行快速排序。
需要注意的是,当数据集很小时,无法进行快速排序,而要使用其它排序算法。显然,当数据集中的数据只有两个或更少时,就不可能将数据集再分割成三个子集。实际上,当数据集比较小时,程序员就应该考虑是否仍然采用快速排序算法,因为在这种情况下另外一些排序算法往往更快。
例3. 2a用快速排序算法重写了例3.1中的字符串数组排序程序,你同样可以将它和本章末尾的有关代码一起编译成一个可执行程序。程序中定义了一个宏,它可使程序更易读,并能加快执行速度。
快速排序算法是由程序中的myQsort()函数实现的,它是按升序对一个字符串数组进行排序的。函数myQsort()的具体工作过程如下:
(1)首先检查最简单的情况。在第17行,检查数组中是否没有或只有一个元素——在这种情况下,数组已经是有序的,函数就可以返回了。在第19行,检查数组中是否只有两个元素——在这种情况下,要么数组已经是按升序排列的,要么交换这两个元素的位置,使它们按升序排列。
(2)在第28行至第53行,将数组分割为两个子集:第一个子集中的数据大于或等于分割值,第二个子集中的数据小于分割值。在第28行,选择数组中间的元素作为分割值,并将其和数组中的第一个元素交换位置。在第37行至第39行,在数组中找到属于第二个子集的第一个元素;在第45行至第47行,在数组中找到属于第一个子集的最后一个元素。在第49行,检查属于第二个子集的第一个元素是否位于属于第一个子集的最后一个元素的后面,如果是,则第一个子集的所有元素都已在第二个子集的所有元素的前面,数据已经划分好了;否则,交换这两个元素的位置,然后重复上述这种检查。
(3)当两个子集分割完毕后,在第55行,将分割值和第一个子集中的最后一个元素交换位置,排序结束时这个分割值将仍然排在现在这个位置。在第57行和第58行,分别调用myQsort()函数对分割所得的子集进行排序。当所有的子集都经过排序后,整个数组也就排好序了。
例3. 2a一个不使用qsort()函数的快速排序算法程序
#include <stdlib.h>
#define exchange(A, B, T) ((T) = (A), (A) = (B),(B)=(T))
/* Sorts an array of strings using quick sort algorithm */
static void myQsort(const char * array[], size_t num)
{
const char * temp
size_t i, j;
/*
* Check the simple cases first:
* If fewer than 2 elements, already sorted
* If exactly 2 elements, just swap them (if needed).
*/
if (num <2)
return;
else if (num==2)
{
if (strcmp(array[O], array[1])>O)
exchange (array[0], array[1] ,temp)
}
/*
* Partition the array using the middle (num/2)
element as the dividing element.
*/
exchange (array[0], array[num / 2], temp)
i=1;
j=num;
for (; ;)
{
/*
* Sweep forward until and element is found that
* belongs in the second partition.
*/
while (i<j && strcmp (array[i], array[0])
<=0)
i++;
/*
* Then sweep backward until an element
* is found that belongs in the first
* partition.
*/
while (i<j&& strcmp(array[j-1], array[O]
>=0)
j--;
/* If no out-of-place elements, you're done */
if (i>=j)
break
/* Else, swap the two out-of-place elements */
exchange(array[i], array[j-l], temp)
}
/* Restore dividing element */
exchange(array[O], array[i-1], temp)
/* Now apply quick sort to each partition */
myQsort (array, i-1 )
myQsort (array + i, num-i)
}
/* Sort strings using your own implementation of quick sort */
void sortStrings (char * array[])
{
/* First, determine the length of the array */
int num
for (num = O; array[num] ; num++ )
myQsort((void * ) array, num)
}