排序 – 插入排序和希尔排序
一、插入排序
1.排序思想
直接插入排序的基本思想是:把待排序的数据按其关键码值的大小,依次插入一个已经排好序的序列中,直到所有的数据都插入完成,得到一个新的有序序列。
如上图所示,再插入第i个数据时,前面的a[0]~a[i-1]个数据已经排好序,只需将a[i] 和前面的i个数据依次比较,比a[i]大的数据直接向后挪,空出当前位置,直到找到比a[i]小的数据或者数组到头后,将a[i]填入空位中,就完成了一次单躺排序。
2.代码示例
代码如下:
void InsertSort(int* a, int sz)
{
for (int i = 0; i < sz - 1; i++)//边界条件是end走到倒数第二个数据,tmp是最后一个
{
//单躺排序:[0, end]有序,把end + 1位置上的数插入,保持有序
int end = i;
int tmp = a[end + 1];//使用tmp保存当前数据,前面的数据就可以直接覆盖
while (end >= 0)
{
if (tmp < a[end])
{
a[end + 1] = a[end];//直接覆盖
--end;
}
else
{
break;
}
}
a[end + 1] = tmp;
}
}
3.特性总结
1.原序列越接近有序,直接插入排序的效率越高;
2.时间复杂度:最好:顺序O(N),最坏:完全逆序O(N^2)
3.空间复杂度:O(1);
4.稳定性:稳定(不会改变相同码值数据的相对顺序)。
二、希尔排序
1.排序思想
希尔排序法又称缩小增量法,其基本思想是:将整个数据以间隔为gap分割成各组数据,然后将各组数据进行插入排序。然后不断减小gap,直到gap = 1时,就成为直接插入排序。
gap > 1时是预排序,gap = 1时是直接插入排序,这样操作的原因是:越有序的数据,插入排序越快
如上图所示,第一趟排序时gap为5,间隔为5的数据为一组,整个序列共分为5组,每组数据都进行插入排序,使各组数据都有序;第二趟gap为2,间隔为2的数据为一组,共2组,再次进行插入排序,直到最后gap为1,进行的就是直接插入排序。
gap由大到小,整个序列不断接近有序,到最后gap = 1时,直接插入排序的效率会很高。
2.代码示例
预排序:
//整个预排序
int gap = 3;//间距为gap是一组数据
for (int i = 0; i < gap; i++)//每次预排序一共排gap组数据
{
//单躺预排序,相当于插入排序
for (int j = i; j < sz - gap; j += gap)//边界条件是:end为倒数第二个数据,所以 j < sz - gap
{
int end = j;
int tmp = a[end + gap];
while (end >= 0)
{
if (tmp < a[end])
{
a[end + gap] = a[end];
end -= gap;
}
else
{
break;
}
}
a[end] = tmp;
}
}
希尔排序:
void ShellSort(int* a, int sz)
{
//gap > 1时是预排序,gap = 1时是直接插入排序
int gap = sz;
while (gap > 1)
{
gap = gap / 3 + 1;//gap由大到小,最后等于1时就是插入排序
//加一的原因是:保证最后一个gap一定是1当上一个gap < 3是,下一个gap就是0了
//整个预排序:i一个一个走,但是每次tmp都是end + gap的数据,还是相当于对间隔为gap的数据进行插入排序
for (int i = 0; i < sz - gap; i++)//边界条件是:end为倒数第二个数据,所以 i < sz - gap
{
int end = i;
int tmp = a[end + gap];
while (end >= 0)
{
if (tmp < a[end])
{
a[end + gap] = a[end];
end -= gap;
}
else
{
break;
}
}
a[end + gap] = tmp;
}
}
}
3.特性总结
1.希尔排序是对直接插入排序的优化;
2.时间复杂度:计算比较复杂,大致为O(N^1.3);
3.空间复杂度:O(1);
4.稳定性:不稳定,可能会打乱相同码值数据之间的相对顺序。
总结
以上就是本次要讲的内容,本文简单介绍了直接插入排序和希尔排序的思想、代码与特性,不足之处还望各位大佬指正。