插入排序的思想:
已经有一个有序的序列,把新的值插入进去,依旧要保持有序,那么就要找对应的位置。
举例,升序:
已经有个有序的序列:2 4 6 9
Case1:插入10(大于所有的数) ——>把10直接插入到9的后面;
Case2:插入5(大于一部分数) ——>先保存9(因为是在数组内排序),把9和6往后挪,把5放到原来6的位置;
Case3:插入0(小于所有的数) ——>先保存9(因为是在数组内排序),把9、6、4、2往后挪,把0放到原来2的位置;
根据这个思路,先写单趟的(排序全部先写单趟,思路清晰),代码如下:
(当然,其他方式的代码也可以,但是不适合写接下来的希尔排序)
int end;//end指向已经有序的最后一个
int tmp = a[end + 1];//保存
while (end >= 0)//边界条件:思考:为何有=0
{
if (tmp < a[end])
{
a[end + 1] = a[end];//往后挪
end--;
}
else
{
break;
}
}
a[end + 1] = tmp;
写全趟,代码如下:
void InsertSort(int* a, int n)// 插入排序
{
for (int i = 0; i<n-1; i++)//注意i的范围是i<n-1还是i<n
{
int end=i;
int tmp = a[end + 1];
while (end >= 0)
{
if (tmp > a[end])
{
break;
}
else
{
a[end + 1] = a[end];
end--;
}
}
a[end + 1] = tmp;
}
}
希尔排序的背景:
给一个降序数组,想把它升序排列,如果按照插入排序的思想,时间复杂度位O(N^2);那么需要优化一下希尔排序,降低它的时间复杂度。
希尔排序的思想:第一步:预排序(分组排序) 第二步:插入排序
gap>1是预排序,gap==1是插入排序,
比如:数组9 8 7 6 5 4 3 2 1
给一个gap,称为间距,不妨设gap=3,gap等于几,数组就被分为了几组
将每组数据进行插入排序:即 9 6 3 插入排序、8 5 2插入排序、7 4 1插入排序;
显然,此时的数组是3 2 1 6 5 4 9 8 7,不是有序的,只是相对的对比原始数据有序了。
代码实现,先把间距为gap的一组的单趟先排好:
int gap;
int end;
int tmp = a[end + gap];//保存end的后一个位置(gap)
while (end >= 0)
{
if (tmp > a[end])
break;
else
{
a[end + gap] = a[end];
end -= gap;
}
}
a[end + gap] = tmp;
把间距为gap的一组的全趟排好,代码如下:
int gap;
for (int i = 0; i < n - gap; i += gap)
{
int end=i;
int tmp = a[end + gap];
while (end >= 0)
{
if (tmp > a[end])
break;
else
{
a[end + gap] = a[end];
end -= gap;
}
}
a[end + gap] = tmp;
}
把间距为gap的所有组的全趟都排完序,此次排完序的内容才是3 2 1 6 5 4 9 8 7,代码如下:
int gap;
for (int j = 0; j < gap; j++)
{
for (int i = j; i < n - gap; i += gap)
{
int end = i;
int tmp = a[end + gap];
while (end >= 0)
{
if (tmp > a[end])
break;
else
{
a[end + gap] = a[end];
end -= gap;
}
}
a[end + gap] = tmp;
}
}
简化一下成两层循环,效率不变的代码:
这种简化的思想是:gap并排排序:(并没有效率的上升)
int gap;
for (int i = 0; i < n - gap; i++)
{
int end = i;
int tmp = a[end + gap];
while (end >= 0)
{
if (tmp > a[end])
break;
else
{
a[end + gap] = a[end];
end -= gap;
}
}
a[end + gap] = tmp;
}
最后,设置gap,gap由大变小,最后为1。
gap越大,大的数可以更快的到后面,小的可以更快的到前面,越不接近有序;(升序)
gap越小,数据跳动越慢,越接近有序。
代码实现如下:
void ShellSort(int* a, int n)// 希尔排序
{
int gap = n;
while (gap > 1)
{
//gap = gap / 2;//gap变化的常用两种方法
gap = gap / 3 + 1;//gap变化的常用两种方法
for (int i = 0; i<n - gap; i++)
{
int end = i;
int tmp = a[end + gap];
while (end >= 0)
{
if (tmp > a[end])
break;
else
{
a[end + gap] = a[end];
end -= gap;
}
}
a[end + gap] = tmp;
}
}
}
希尔排序的时间复杂度,与gap有关,很难去计算。
结论:O(N^1.3)。