实现代码中会出现宏
typedef int DataType;
#define SWAP( A, B ) {DataType x = A; A = B; B = x;}
1. 插入排序算法
类似数学归纳法的描述:
① 起始时A[beg]的元素位置不变(此时A[beg]这一个元素已经有序)
② 假设A[beg]..A[i](beg<=i<=end-1)已经排好序, 对A[i+1]..A[end],依次将A[i+1],...,A[end]插入到A[beg],...,A[i]的指定位置
将①②结合可得到插入排序的伪代码:
for i=beg+1:1:end
t = A[i]
for j=i-1:-1:beg
if t < A[j]
A[j+1] = A[j]
else
break
A[j+1] = t
void InsertSort( DataType* A, int beg, int end )
{
int i, j;
int k;
DataType t;
for ( i = beg+1; i <= end; ++i )
{
t = A[i];
for ( j = i-1; j >= beg; --j )
{
if ( A[j] > t )
{
A[j+1] = A[j];
}
else
{
break;
}
}
A[j+1] = t;
}
}
2.选择排序
基本思想,类似数学归纳法的描述:
① 起始时找到A[beg]..A[end]中的最小元素,将该元素与A[beg]交换;
② 假设A[beg]..A[i](beg<=i<=end-1)已经有序且A[i+1]..A[end]的任一元素大于A[beg]..A[i], 从A[i+1]..A[end]选择最小的元素添加到A[beg]..A[i]末尾,亦即将A[i+1]与A[i+1]..A[end]中的最小元素交换位置
将①②结合可得到选择排序的伪代码:
for i=beg:1:end-1
find k s.t. A[k] = min( A[i+1]..A[end] )
exchange A[i] and A[k]
实现:
void SelectionSort( DataType* A, int beg, int end )
{
int i, j, k;
DataType m;
for ( i = beg; i < end; ++i )
{
m = A[i];
k = i;
for ( j = i+1; j <= end; ++j )
{
if ( A[j] < m )
{
m = A[j];
k = j;
}
}
if ( k != i )
{
SWAP( A[i], A[k] );
}
}
}
3. 冒泡排序
基本思想,类似数学归纳法的描述:
① 起始进行第一轮冒泡,对A[beg]..A[end]从后往前,如果A[j-1]>A[j], 则交换A[j-1]和A[j]的值(beg<=j-1<j<=end);完成后A[beg]已经为数组中的最小元素
② 假设A[beg]..A[i](beg<=i<=end-1)已经排好序且A[i+1]..A[end]的任一元素大于A[beg]..A[i],对A[i+1]..A[end]从后往前冒泡:若A[j-1]>A[j],交换A[j-1]和A[j], (i+1<=j-1<j<=end)
将①②结合可得到冒泡排序伪代码:
for i = beg:1:end-1
for j = end:-1:i+1
if A[j-1] < A[j]
then exchange A[j-1] and A[j]
实现:
void BubbleSort( DataType* A, int beg, int end )
{
int i, j;
int k;
for ( i = beg; i < end; ++i )
{
for ( j = end; j > i; --j )
{
if ( A[j-1] > A[j] )
{
SWAP( A[j-1], A[j] );
}
}
}
}
4. 快速排序
基本思想:分治策略
首选选取一个枢纽元P,通常选取P=A[beg],将数组余下的元素通过与P比较,将原数组划分为两部分:小于P的部分和大于P的部分,然后构建新的数组,使得A[j]的元素值为P,A[beg]..A[j-1]的值全都小于P,A[j+1]..A[end]的值全部大于P;然后分别对子数组A[beg]..A[j-1]和A[j+1]..A[end]进行快速排序。
实现如下(递归版本):
void QuickSort( DataType* A, int beg, int end )
{
int i, j;
DataType p;
if ( beg >= end )
return;
i = beg;
j = end+1;
p = A[beg];
for ( ;; )
{
while( ++i <= end && A[i] < p );
while( A[--j] > p );
if ( i > j )
break;
SWAP( A[i], A[j] );
}
SWAP( A[j], A[beg] );
QuickSort( A, beg, j-1 );
QuickSort( A, j+1, end );
}
对以上基本的快速排序算法可进行一下两方面的改进:
① 枢纽元P的选择对快速排序算法的效率有很大影响,为了使得快速排序算法对所有可能情况的排序时间复杂度尽可能低,通常在数组中选取一个随机的元素作为枢纽元;具体的实现策略也很简单,就是现在数组中随机选取一个元素,然后将该元素与数组第一个元素交换,并将该元素作为枢纽元,接下来便可直接套用如下实现的算法;
② 虽然插入排序等时间复杂度为O(N^2)的算法比快速排序的平均时间复杂度O(NlogN)要大,但经过试验测试,当待排序的数据个数小于20个时,插入排序等算法的实际消耗时间比快速排序算法要小(因为快速排序算法是一个递归的算法,通常递归的效率是比较低的);因此可以考虑当快速排序递归到数组元素个数小于某一值N(N<20)时,不再采用快速排序继续递归,而是采用插入排序等算法。
随机选取枢纽元素的快速排序算法实现:
#include <stdlib.h>
#include <time.h> // windows
void QuickSort1( DataType* A, int beg, int end)
{
int i, j, n;
DataType p;
if ( beg >= end )
return;
srand((unsigned)time(0)); // 生成随机数种子
n = rand()%(end-beg+1) + beg; // 生成beg..end之间的随机数
SWAP(A[n], A[beg]);
p = A[beg];
i = beg;
j = end+1;
for ( ;; )
{
while( ++i <= end && A[i] < p );
while( A[--j] > p );
if ( i > j )
break;
SWAP( A[i], A[j] );
}
SWAP( A[beg], A[j] );
QuickSort1( A, beg, j-1 );
QuickSort1( A, j+1, end );
}
当数组元素小于16时采用插入排序算法,实现如下:
void QuickSort2( DataType* A, int beg, int end )
{
int i, j;
DataType p;
if ( beg >= end )
return;
else if ( end - beg < 16 )
{
InsertSort( A, beg, end );
return;
}
p = A[beg];
i = beg;
j = end+1;
for ( ;; )
{
while( ++i <=end && A[i] < p );
while( A[--j] > p );
if ( i > j )
break;
SWAP( A[i], A[j] );
}
SWAP( A[beg], A[j] );
QuickSort2( A, beg, j-1 );
QuickSort2( A, j+1, end );
}
综合 QuickSort1 和 QuickSort2 的快速排序算法实现:
void QuickSort3( DataType* A, int beg, int end )
{
int i, j, n;
DataType p;
if ( beg >= end )
return;
else if ( end - beg < 16 )
{
InsertSort( A, beg, end );
return;
}
srand( (unsigned int)time(0) );
n = rand()%(end-beg+1) + beg;
SWAP( A[beg], A[n] );
i = beg;
j = end+1;
p = A[beg];
for ( ;; )
{
while( ++i <= end && A[i] < p );
while( A[--j] > p );
if ( i > j )
break;
SWAP( A[i], A[j] );
}
SWAP( A[beg], A[j] );
QuickSort3( A, beg, j-1 );
QuickSort3( A, j+1, end );
}
快速排序算法的非递归实现可参见:快速排序算法的递归与非递归实现 。