预备知识:基于比较的排序本质就是将序列中所有的逆序消除.一个排序好的序列是没有逆序的.
定理:N个互异数的数组的平均逆序数是N(N-1)/4.
通过交换相邻元素进行排序的任何算法平均时间复杂度为N^2.
(因为每次交换只减少一个逆序,而初始的平均逆序数是N(N-1)/4==>时间复杂度为N^2)
1)插入排序 ,冒泡排序,选择排序==>交换相邻元素的方法
//下面只贴出插入排序程序
//两种实现一样.第一种容易理解,但是花去了更多的时间.第二种是对第一种的优化.
void InsertionSort(ElementType A[], int N)
{
int j, P;
ElementType Tmp;
for(P = 1; P < N; P ++ )
{
for( j = P; j > 0; j -- )
{
if( A[ j - 1 ] > A[ j ] )
{
Tmp = A[ j ];
A[ j ] = A[ j - 1 ];
A[ j - 1 ] = Tmp;
}
}
}
}
void InsertionSort(ElementType A[], int N)
{
int j,P;
ElementType Tmp;
for( P = 1; P < N; P ++ )
{
Tmp = A[ P ];
for( j = P; j > 0 && A[ j - 1 ] > Tmp; j -- )
A[ j ] = A[ j - 1 ];
A[ j ] = Tmp;
}
}
2)希尔排序
原理:通过比较相距一定间隔的元素来工作;隔趟比较所用的距离随着算法的进行而减少,
直到只比较相邻元素的最后一趟排序为止.由于这个原因,希尔排序也叫缩小增量排序.
Shell建议的序列是:H = [N/2];实践中最好的序列之一是{1,5,19,109 .....}也就是 4^i - 3*2^i + 1.
通过将这些值放到数组中可以很容易实现该算法.
//增量为H=N/2
void Shellsort(ElementType A[], int N )
{
int i, j, Increment;
ElementType Tmp;
for( Increment = N / 2; Increment > 0; Increment /= 2 )
{
for( i = Increment; i < N; i ++ )
{
for( j = i; j >= Increment; j -= Increment )
{
if( A[ j ] < A[ j - Increment ] )
{
Tmp = A[ j ];
A[ j ] = A[ j - Increment ];
A[ j - Increment ] = Tmp;
}
else
break;
}
}
}
}
3)快速排序
原理:随机的(一般用三数中值分割法)选取枢纽元,集合中其余元素分成两个更小的集合.
大于枢纽元的放到一个集合,小于枢纽元的放到另一个集合.然后递归地将两个子集合排序.
对于很小的数组(N<=20)快速排序不如插入排序好.因为快速排序是递归的,所以这种情况免不了要发生.
通常解决的方法是对于小的数组不递归地使用快速排序,而是用插入排序.
void Swap( ElementType *Left, ElementType *Right )
{
ElementType Tmp = *Left;
*Left = *Right;
*Right = Tmp;
}
//三数中值分割法获取枢纽
//例如:1 3 6 4 2,它的左边元素是1,右边是2,中间元素是6.所以中值自然是2.
//
ElementType Median3( ElementType A[] ,int Left, int Right )
{
int Center = (Left + Right ) / 2;
if(A[ Left ] > A[ Center ])
Swap(&A[Left],&A[Center]);
if(A[Left] > A[Right])
Swap(&A[Left],&A[Right]);
if(A[Center] > A[Right])
Swap(&A[Center],&A[Right]);//1 3 2 4 6
Swap(&A[Center],&A[Right - 1]);//将枢纽元存放到A[Right - 1]的位置,方便快速排序的元素的分割. 1 3 4 2 6
return A[Right - 1];//返回枢纽元 2
}
#define Cutoff (20)
void Qsort( ElementType A[], int Left, int Right )
{
int i,j;
ElementType Pivot;
if( Left + Cutoff <= Right )//限定使用快速递归排序的元素个数不小于Cutoff,否则使用插入排序.
{
Pivot = Median3(A,Left,Right);
i = Left; j = Right - 1;
for(;;)//分割实现:将所有小于枢纽元的元素移动到数组左边,所有大元素移动到数组的右边.
{//分割停止时,i指向一个大元素,j指向一个小元素.
while(A[++i] < Pivot){}//i遇到>=Pivot的元素时,跳出循环,准备交换
while(A[--j] > Pivot){}//j遇到<=Pivot的元素时,跳出循环,准备交换
if( i < j)
Swap(&A[i],&A[j]);
else
break;
}
Swap(&A[i],&A[Right - 1]);//分割的最后一步是将枢纽元与i所指向的元素交换.
Qsort(A,Left,i - 1);//对分割好的子集合进行快速递归排序
Qsort(A,i + 1, Right );
}
else //使用插入排序
InsertionSort(A + Left, Right - Left + 1);
}
插入,希尔,快速排序
最新推荐文章于 2023-04-01 20:49:07 发布