首先回顾插入排序:
- 算法思想:
将待排序列分成两个子序列,其中一个有序,另外一个无序。
每次从无序序列选取第一个元素,插入到有序序列中,直到所有的元素全部插入。 - 实现
待排序列:L[1…N]
开始时,有序序列为空,无序序列为待排序列。
可以假设待排序列中的第一个元素形成的序列为有序序列L[1](因为一个元素自身肯定是有序的,剩下的N-1个元素形成的是无序序列L[2…N]。这样总共就需要插入N-1次。即外层循环需要N-1次。
一般的,
从待排序的序列L[p…N]中选取第一个元素L[p],记录temp = L[p]
(因为p这个位置也是插入的位置,可能会被前面的元素移动覆盖),寻找合适的插入点i
(i可以看成是当前待插入的位置,从i=p开始,比较的时候从A[i-1]开始比较,终止条件是i>=1,因为每次都需要i–)也就是元素右移,判断条件是temp<A[i-1]。
#include<stdio.h>
void Insertion_Sort(int A[],int N)
{
int p,i;
int temp;
for( p = 1;p<N;p++)//选取下一个插入的数字
{
temp = A[p];
for(i = p;i>=1 && temp < A[i-1];i--)//移动寻找插入位置
{
A[i] = A[i-1];
}
A[i] = temp;//将数据插入
}
}
int main()
{
int A[8] = {5,6,1,2,8,4,9,3};
Insertion_Sort(A,8);
for(int i = 0;i<7;i++)
{
if(i == 7)
printf("%d\n",A[i]);
else
printf("%d ",A[i]);
}
return 0;
}
- 优点:针对数据基本有序,后者数据令比较小的时候插入排序很有效
- 问题来了?能不能找到针对较大规模的数并且数据无序的排序算法?
希尔排序
首先是为什么需要希尔排序?因为插入排序中每次只能够消除一个逆序对(即每次都只是相邻元素的交换)。要想算法更快需要一次能够消除多个逆序对即每次交换相隔较远的两个元素。因此希尔排序应运而生。
步骤:
定义一个增量序列Dm,Dm-1…D1;
每次对增量序列进行Dk间隔排序。
注意:每次Dk-1间隔的排序之后不会影像之前的Dk间隔排序
- 原始的希尔排序:刚开始的增量序列为D = N/2,以后D=D/2;
代码如下:
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define MAXN 20000
void print(int A[],int N)
{
int i;
for(i = 0;i<N;i++)
{
if(i == N-1)
printf("%d\n",A[i]);
else
printf("%d ",A[i]);
}
}
void Insertion_Sort(int A[],int N)
{
int p,i;
int temp;
for( p = 1;p<N;p++)//选取下一个插入的数字
{
temp = A[p];
for(i = p;i>=1 && temp < A[i-1];i--)//移动寻找插入位置
{
A[i] = A[i-1];
}
A[i] = temp;//将数据插入
}
}
void Shell_Sort(int A[],int N)
{
int D,p,i;
int temp;
for(D = N/2;D>0;D/=2)//增量序列
{
for(p = D;p<N;p++)//选取下一个插入的数字
{
temp = A[p];
for(i = p;i>=D&&temp<A[i-D];i-=D)//移动寻找插入位置
{
A[i] = A[i-D];
}
A[i] = temp;
}
}
}
int main()
{
int i;
int A1[MAXN],A2[MAXN];
clock_t start,end;
srand((unsigned)time(NULL));
for(i = 0;i<MAXN;i++)
{
A1[i] = rand();
A2[i] = A1[i];
}
start = clock();
Insertion_Sort(A1,MAXN);
//print(A1,MAXN);
end = clock();
printf("%lf s\n",(double)(end-start)/CLOCKS_PER_SEC);
start = clock();
Shell_Sort(A2,MAXN);
//print(A2,MAXN);
end = clock();
printf("%lf s\n",(double)(end-start)/CLOCKS_PER_SEC);
return 0;
}
/*
不同的数据量的比较
MAXN = 20000
0.283000 s
0.005000 s
MAXN = 80000
5.085000 s
0.022000 s
MAXN = 100000
7.428000 s
0.028000 s
*/
其中有一个需要注意的点是:
在这里和我画了一张图说明
这里有一篇很好的博客说明希尔排序:希尔排序
但是这种情况的最坏时间复杂度是什么呢?
举个坏例子:之前的大于1的增量序列不起作用,最后还是需要靠完全的插入排序进行。
解决的方法是:使用特定的增量序列。
使用增量序列的代码实现:
#include<stdio.h>
#include<stdlib.h>
#include<time.h>
#define MAXN 80000
void print(int A[],int N)
{
int i;
for(i = 0;i<N;i++)
{
if(i == N-1)
printf("%d\n",A[i]);
else
printf("%d ",A[i]);
}
}
void Insertion_Sort(int A[],int N)
{
int p,i;
int temp;
for( p = 1;p<N;p++)//选取下一个插入的数字
{
temp = A[p];
for(i = p;i>=1 && temp < A[i-1];i--)//移动寻找插入位置
{
A[i] = A[i-1];
}
A[i] = temp;//将数据插入
}
}
void Shell_Sort(int A[],int N)
{
int Si,D,p,i;
int temp;
/* 这里只列出一小部分增量 */
int Sedgewick[] = {929, 505, 209, 109, 41, 19, 5, 1, 0};
for ( Si=0; Sedgewick[Si]>=N; Si++ )
; /* 初始的增量Sedgewick[Si]不能超过待排序列长度 */
for(D = Sedgewick[Si];D>0;D=Sedgewick[++Si])//增量序列
{
for(p = D;p<N;p++)//选取下一个插入的数字
{
temp = A[p];
for(i = p;i>=D&&temp<A[i-D];i-=D)//移动寻找插入位置
{
A[i] = A[i-D];
}
A[i] = temp;
}
}
}
int main()
{
int i;
int A1[MAXN],A2[MAXN];
clock_t start,end;
srand((unsigned)time(NULL));
for(i = 0;i<MAXN;i++)
{
A1[i] = rand();
A2[i] = A1[i];
}
start = clock();
Insertion_Sort(A1,MAXN);
//print(A1,MAXN);
end = clock();
printf("%lf s\n",(double)(end-start)/CLOCKS_PER_SEC);
start = clock();
Shell_Sort(A2,MAXN);
//print(A2,MAXN);
end = clock();
printf("%lf s\n",(double)(end-start)/CLOCKS_PER_SEC);
return 0;
}