在数据结构中,排序是处理数据中经常用到 ,而排序又分为内排序和外排序。内排序指的是排序期间,数据全部放在内存中进行排序;而外排序则是因为在排序期间全部对象个数太多,不能同时放在内存中,在排序的过程中不断进行内外存之间移动的排序。选择合适排序算法这个时候就非常重要,可以帮助我们减少时间、空间等一些可避免的开销。我们所说八大排序算法指的都是内部排序,今天先来看插入排序。
插入排序常用的有直接插入排序和希尔排序,排序算法的基本思想是:每步都将待排序的对象,按照其关键码大小,插入到前面已经排好序的一组数据的适当位置上,直到所有的对象全部插入为止。下来我们详细说说直接插入排序和希尔排序。
1.直接插入排序
基本思想:将一个数据插入到已排好有序表中,从而得到一个新的,数据的数量随之增加。也就是说,先将第一个数据看成是有序的,然后将第二个数据进行插入,直到所有的数据被插入进去变得有序。
重点:设置一个边界监视,通过不断缩小边界来实现插入到合适的位置。
时间复杂度:O(n^2)
动图展示如上,也就是每步都是将这个元素直接插入到合适的位置,分步展示如下。
在这里如果是插入一个和插入数据相等的数,那么这个相等的数会被插入到相等元素的后面。所以,相等元素的先后顺序没有发生改变,所以插入排序是稳定的。算法实现如下。
void InsertSort(int* a, size_t n)
{
assert(a);
for (size_t i = 0; i < n-1; i++)
{
int tmp = a[i + 1];
int end = i;
while (end >= 0)
{
if (a[end]>tmp)
{
a[end + 1] = a[end];
--end;
}
else
break;
}
a[end + 1] = tmp;
}
}
2.希尔排序
基本思想:先将要排序的数据组分割成若干个子数据组,分别进行插入排序,等整个排序的数组基本有序时,在对数据依次进行插入排序。
重点:合理选择分割的数组数(增量因子)
动图展示希尔排序思想如上,下面就一个实际例子分布展示。
希尔排序的时效性分析比较难,比较次数与增量因子和移动次数相关。目前增量因子的取法比较多,但值得注意的是最后一个增量因子必须是1。并且希尔排序是一个不稳定的排序方法。希尔排序的算法实现如下。
void ShellSort(int* a, size_t n)
{
assert(a);
int gap = n;
while (gap > 1)
{
gap = gap / 3 + 1;
for (size_t 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];
else
break;
end -= gap;
}
a[end + gap] = tmp;
}
}
}
总的来说,插入算法的实质都是一样的,只是因为在选择不同的方法对已经排好序的有序数据寻找位置进行插入。