前言
这几天耗费了大量的时间去理清排序类算法的实现以及复杂度,对排序也算是略懂一二,下面博主就进行详细的介绍算法的思路与实现过程的代码。
基于插入的排序算法
这一类算法类似于,有序的整理扑克牌,将牌不断插入从而形成有序的牌堆。
插入类排序与之类似,其主要分为直接插入排序以及衍生出来的希尔排序。
直接插入排序
使用最后一个元素与其数组其他元素通过遍历比较,找到需要插入的位置将其插入即可,这里需要注意的是,当元素插入时,插入位置的后面元素需要后移。
显而易见,要实现有序,则先进行第一个数与第二个数进行比较,得到一组元素大小为二的有序数组再与其第三个进行遍历比较,以此迭代来达到整个数组有序的效果。代码如下:
// 插入排序
void InsertSort(int* a, int n)
{
assert(a);
//假如是n个元素,只需要循环n-1次即可
for (int i = 0; i < n - 1; i++)
{
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;
}
}
它的时间复杂度从代码中可以看出嵌套了两个循环,最外为遍历趟数,里面为遍历次数,最后一趟遍历的次数最坏为n-1,倒数第二趟遍历的次数最坏为n-2,依次类推可以得到时间复杂度为O(N^2)。
它的空间复杂度为常数,即O(1)。
它是一种稳定的排序算法,何为稳定?稳定大概的意思就是他的相对位置不变,举个例子,如下。
{
1, 2, 5(1), 3, 5(2), 6, 5(3)};
排序后;
{
1, 2, 3, 5(1), 5(2), 5(3), 6};
上述数组有三个相同的数字5(括号为他们的相对位置下标),当排序完成之后,他们的相对位置如果不变即为稳定。所以在直接插入排序算法中,可以做到排序后相对位置不变,即具有稳定性。
另外这个算法适用于接近有序的时候使用,时间复杂度会大大降低,假设数组有序,虽然遍历趟数没变,但是每一趟的遍历次数只有一次,所以它适用于有序或者接近有序的时候使用。
希尔排序
这种排序又被称作为缩小增量排序,其实就是对直接插入排序的优化,使数组达到接近有序再使用直接插入排序而达到减小时间复杂度的算法。经过直接插入排序的分析得知,它是一种没有间距的直接遍历比较,即元素元素之间的间距gap为0。如果能增加间距gap,将数组后面小的数据以最快的速度放到前面,数组前面大的数据以最快的速度放到后面,从而达到数组接近有序的效果,此时再使用直接插入排序即可减小时间复杂度。
运用间距gap是为了达到预排序的目的,例如,gap=5,则将元素位置之间相差5的元素分组拿出来进行排序,如上图所示,94、18、26、53、75被单独拿出来进行插入排序。代码的具体实现如下:
void ShellSort(int* a, int n)
{
assert(a);
//gap > 1为预排序
//gap = 1直接插入排序
int gap = n;
while (gap > 1)
{
//经过前人经验推算gap/3是最优预排序
//+1是为了保证最后一次是1
gap = gap / 3 + 1;
//多组并排同时排序
for (int i = 0; i < n - gap; i++)
{
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;
}
}
}
这个代码有个比较巧妙的点就是,多组并排,在循环中同时进行多组排序,这样就不需要单独把元素拿出来形成单独的一组进行单独的插入排序。大家好好意会一下。
时间复杂度不太好算,但是经过推导一般是在O(N^1.3 ~ N^2)之间,空间复杂度为O(1),但是它并不稳定,因为gap预排序很容易调换其相对位置。
基于选择的排序算法
直接选择排序
如上图所示,对大小为n的数组进行遍历,选出最小(最大)的值放入数组的开头(结尾),遍历n-1趟,第一趟遍历次数为n-1,后续第二次为n-2,依次类推可得时间复杂度为O(N^2),空间复杂度为O(1),虽然思路很好理解,但算是也是不稳定的,如下。
相对位置改变,代码实现如下:
void Swap(int* left, int* right)
{
int tmp = *left;
*left = *right;
*right = tmp;
}
// 选择排序
void SelectSort