在了解希尔拍排序之前我们首先要了解插入排序。希尔排序的实现是建立在插入排序的基础之上的。插入排序本身不是一种效率非常高的排序算法,时间复杂度为,但他提够了一种可以优化的思路。
一(1).插入排序
这里有一张插入排序的逻辑图。可以直观的看到插入排序的实现过程。
一(2).代码实现
首先有一个end角标和一个end+1角标,先让end角标指向数组的第一个位置,他表示的是有序数组的末尾,(一个数本身肯定是有序的嘛)end+1角标就是你要下一次要插入的值。先将要插入的值保存下来,然后每次把要插入的值和end角标的值比较,若不符合判断条件就把前面end的数覆盖到end+1的位置上。当遇到判断条件不符合要求时跳出。此时数组内有一个空余位置,就是应该插入的位置,直接把值复制过去就行了。
这样我们就完成插入一个数的整个过程。至于排序就是把每个数都插入一次
void Insertsort(int *arr,int len)
{
int i = 0;
for (i = 0; i < len-1; i++)
{
int end=i;
int tmp = arr[end + 1]; //先把要插入的值保存下来
while (end >= 0)
{
if (tmp < arr[end]) //对比找要插入的位置
{
arr[end + 1] = arr[end]; //满足判断条件就把前一个数覆盖到后一个数上覆盖过去
end--; //跑遍整个数组挨个挪动位置最后数组当中会多出一个空位置就是要插入的位置
}
else
{
break;
}
}
arr[end+1] = tmp; //遍历的时候end--了,所以最后覆盖的时候要加1
}
}
二.希尔排序
插入排序在遇到有序数组或者类有序数组时,是具有一定优势的,所以我们可以通过先将无需数组转化为类有序数组,后进行插入排序。我们可以将数组分成若干组。每个组分别进行插入排序
这张图就是将数组分成四组,每组分别进行插入排序。这样就可以得到一个类有序数组,他的代码实现方式和插入可以说是完全一直,只不过是隔开的,所以插入排序里的自增就变成了加上间隔,gap.
for (int j = 0; j < gap; j++)
{
for (i = j; i < len - gap; i += gap)
{
int end = i;
int tmp = arr[end + gap];
while (end >= 0)
{
if (tmp < arr[end])
{
arr[end + gap] = arr[end];
end -= gap;
}
else
{
break;
}
}
arr[end + gap] = tmp;
}
}
经过这段代码我们就可以得到一个近似有序的数组。我们可以通过不断缩小间隔的方式,左后当gap=1的时候,就变成了插入排序。这样就能得到一个完全有序的数租了。
int i;
int gap = len;
while (gap > 1)
{
gap = gap / 3 + 1;
//这里是转换成近似有序数组的代码
}
三.小结
希尔排序的时间复杂度比较难以计算。具体值是在o左右,希尔排序的效率是非常高的。属于T0级别。自己测一下你就知道希尔排序有多牛逼了。我认为希尔排序的重点还是要吃透插入排序。搞懂了插入排序,希尔也小case。