OK,要完成一个排序,第一步是画图并写出第一堂排序。
OK,上面有一个有序数组,现在我要分别将 1和3分别插入到这个数组;以第一组为例,我定义了一个end,将 1和a[end]进行比较,如果a[end]大,则向后移一位,并且end--,a[end]小或者相等,则插入a[3],最后如下图所示
此时end为 -1,我们将它插入到a[0]位置中,即a[end+1]的位置中;
第二种情况最后结束的标志是
此时 3 要插入的位置依旧为 a[end+1] ,所以,综上两种情况,这个数据要插入的位置为a[end+1];
代码如下:
void InsertSort(int* a,int n)
{
//在此以升序为主
//注意,这是一趟的排序 且要求这个数组必须是有序的
int end; //最后一个位置
int tmp = a[end+1]; //要插入的数据,先保存起来
while(end>=0) //因为end的极限为 -1
{
if(a[end] > tmp)
{
a[end+1] = a[end] //后移一位
end--;
}
else
{
break; //找到了插入位置
}
}
a[end + 1] = tmp; //两种情况,要么end = -1,要么 a[end] < tmp;
}
OK,第一趟排序我们已经完成了,上面有个条件,即要求被插入的这个数组需要时有序的。
那么如何确保是有序的呢? 相信你已经知道了,从数组的第一个元素开始不久OK了吗?每个元素对于他自身来说都是有序的,So,代码如下:
void InserSort(int *a,int n)
{
assert(a);
int i = 0;
for(i = 0;i < n-1;i++)
{
int end = i; //从第一个元素开始排;
int tmp = a[end + 1];
while(end >= 0)
{
if(a[end] > tmp)
{
a[end + 1] = a[end];
end--;
}
else
{
break;
}
}
a[end + 1] = tmp;
}
}
那么插入排序的时间复杂度是:O(N^2);
空间复杂度为 O(1);
如果插排在 顺序有序或者接近有序的条件下的时间复杂度会达到O(N),所以插排还是有价值的.
直接插入排序的稳定性:不稳定;
希尔排序 --- 可以认为是 直接插入排序的 优化,是一个很强的排序。
它分为 多次预排序(gap > 1)和直接插入排序(gap == 1);
同上,首先我们应该写入他的第一趟排序 :
void ShellSort(int *a,int n)
{
int gap = 3;
int end = 0;
int tmp = a[end + gap]
while(end >= 0)
{
if(a[end]>tmp)
{
a[end + gap] = a[end];
end -= gap;
}
else
{
break;
}
}
a[end + gap] = tmp;
}
上面的代码已经排了其中一趟中的一个数据,那么如何控制图中红色的那一趟数据呢?OK
是不是只要加一个变量I ,I += gap 进行遍历就可以了,代码如下
void ShellSort(int *a,int n)
{
int i = 0;
int gap = 3;
for(i = 0;i < n - gap;i+=gap)
{
int end = i;
int tmp = a[end + gap]
while(end >= 0)
{
if(a[end]>tmp)
{
a[end + gap] = a[end];
end -= gap;
}
else
{
break;
}
}
}
a[end + gap] = tmp;
}
上述代码已经将其中的一趟排序(红色那一趟)好了,那如何控制gap趟呢?OK,再用一个变量就可以了,代码如下
void ShellSort(int *a,int n)
{
int gap = 3;
int j = 0;
for(j = 0;j < gap;j++)
{
int i = j;
for(i = j;i < n - gap;i+=gap)
{
int end = i;
int tmp = a[end + gap]
while(end >= 0)
{
if(a[end]>tmp)
{
a[end + gap] = a[end];
end -= gap;
}
else
{
break;
}
}
}
a[end + gap] = tmp;
}
}
当然还有一种方式,就是一锅炖,不分几趟,挨个挨个的来,OK代码如下
void ShellSort(int *a,int n)
{
int gap = 3;
for(i = 0;i < n - gap;i++)
{
int end = i;
int tmp = a[end + gap]
while(end >= 0)
{
if(a[end]>tmp)
{
a[end + gap] = a[end];
end -= gap;
}
else
{
break;
}
}
}
a[end + gap] = tmp;
}
此时就完成了希尔排序的预排序,但其中也有一些我们需要知道的,例如,我们这个预排序的
时间复杂度是多少:最好 O(N),最坏呢: F(N + gap) = (1+2+3+...+N/gap)*gap;
gap越大,预排越快,预排后越不接近有序;
gap越小,预排越慢,预排后越接近有序;
不难发现,当gap 为 1时 这就是个直接插入排序 时间复杂度为 O(N^2),
OK现在既然预排序处理完了,那么接下来如何控制多组预排序和直接插入排序呢?
很简单,控制gap不久OK了吗?当gap>1时 多组预排,gap == 1时为直接插入排序
代码如下:
void ShellSort(int *a,int n)
{
int gap = n
while(gap > 1)
{
gap /= 2;
//gap = gap / 3 + 1 //确保最后一次为直接插入排序
for(i = 0;i < n - gap;i++)
{
int end = i;
int tmp = a[end + gap]
while(end >= 0)
{
if(a[end]>tmp)
{
a[end + gap] = a[end];
end -= gap;
}
else
{
break;
}
}
}
a[end + gap] = tmp;
}
}
那么希尔排序的时间复杂度是多少呢 ?
希尔排序时间复杂度为:O(N*logN);
参考了一些资料,如《数据结构(C语言班)》--严蔚敏
希尔排序平均时间复杂度为 : O(N^1.3);
希尔排序的稳定性:不稳定;
最后通过一组实验数据来证明一下二者的差距:
上述时间单位为ms,由此可见希尔排序对直接插入排序的优化是显而易见的。