常见排序算法
本篇介绍前四种,在之后博客中会讲到交换排序和归并排序以及计数排序

基本思想
直接插⼊排序是⼀种简单的插⼊排序法,其基本思想是:把待排序的记录按其关键码值的⼤⼩逐个插⼊到⼀个已经排好序的有序序列中,直到所有的记录插⼊完为⽌,得到⼀个新的有序序列。

动图理解:

int a[] = { 5, 3, 9, 6, 2, 4, 7, 1, 8 };结合以上分析,很容易写出代码:
void InsertSort(int* arr, int n)
{
//n-2
for (int i = 0; i < n - 1; i++)
{
int end = i;
int tmp = arr[end + 1];
while (end >= 0)
{
if (arr[end] > tmp)
{
arr[end + 1] = arr[end];
end--;
}
else {
break;
}
}
arr[end + 1] = tmp;
}
}时间复杂度分析:
空间复杂度:O(1)
在直接插入排序中我们发现,元素越无序,直接插入排序算法时间效率越低(因为比较次数越多)。特别是当数组为降序,我们要排升序,此时数组的相对无序程度达到了最大,时间复杂度也到了最大
所以我们有没有办法对这样一种情况进行优化呢?
答案就是希尔排序,它由D.L.Shell在1959年提出,也是第一批时间复杂度突破了O(n2)的算法之一(致敬🫡)
基本思想:
希尔排序法⼜称缩⼩增量法。
希尔排序法的基本思想是:先选定⼀个整数(通常是gap=n/3+1),把 待排序⽂件所有记录分成各组,所有的距离相等的记录分在同⼀组内,并对每⼀组内的记录进⾏排序,然后gap=gap/3+1得到下⼀个整数,再将数组分成各组,进⾏插⼊排序,当gap=1时,就相当于直接插⼊排序。它是在直接插⼊排序算法的基础上进⾏改进⽽来的,综合来说它的效率是要⾼于直接插⼊排序算法的。

//希尔排序时间复杂度:O(n^1.3)
void ShellSort(int* arr, int n)
{
int gap = n;
while (gap > 1)
{
gap = gap / 3 + 1;//保证最后一次gap一定为1
for (int i = 0; i < n - gap; i++)
{
int end = i;//n-gap-1
int tmp = arr[end + gap];
while (end >= 0)
{
if (arr[end] > tmp)
{
arr[end + gap] = arr[end];
end -= gap;
}
else {
break;
}
}
arr[end + gap] = tmp;
}
}
}时间复杂度分析:
外层循环的时间复杂度可以直接给出为:O(log2n) 或者O(log3n) ,即O(logn)

假设⼀共有n个数据,合计gap组,则每组为n/gap(大致)个;在每组中,插⼊移动的次数最坏的情况下为 S=1 + 2 + 3 + ……+ (n/gap-1),⼀共是gap组,因此:
总计最坏情况下移动总数为:gap ∗ S
gap取值有(以除3为例):n/3 n/9 n/27 … 2 1
一一带入

因此,希尔排序在最初和最后的排序的次数都为n,即前⼀阶段排序次数是逐渐上升的状态,当到达某⼀顶点时,排序次数逐渐下降⾄n,⽽该顶点的计算暂时⽆法给出具体的计算过程
希尔排序时间复杂度不好计算,因为 gap 的取值很多,导致很难去计算,因此很多书中给出的希尔排序的时间复杂度都不固定。《数据结构(C语⾔版)》—严蔚敏书中给出的时间复杂度为:

在希尔排序开始时增量较大,分组较多,每组的记录数目少,n小,此时直接插入最好和最坏时间复杂度n2差距很小,故各组内直接插入较快,后来增量di逐渐缩小,分组数逐渐减少,而各组的记录数目逐渐增多,但由于已经按di-1作为距离排过序,使文件较接近于有序状态,所以新的一趟排序过程也较快。
基本思想
每⼀次从待排序的数据元素中选出最⼩(或最⼤)的⼀个元素,存放在序列的起始位置,直到全部待排序的数据元素排完。
动图解释:

void SelectSort1(int* arr, int n)
{
for (int i = 0; i < n; i++)
{
int mini = i;
for (size_t j = i+1; j < n; j++)
{
if (arr[j] < arr[mini])
mini = j;
}
Swap(&arr[i], &arr[mini]);
}
}void SelectSort(int* arr, int n)
{
int begin = 0;
int end = n - 1;
while (begin < end)
{
int mini = begin, maxi = begin;
for (int i = begin+ 1; i <= end; i++)
{
if (arr[i] > arr[maxi])
{
maxi = i;
}
if (arr[i] < arr[mini])
{
mini = i;
}
}
//mini begin
//maxi end
Swap(&arr[mini], &arr[begin]);
Swap(&arr[maxi], &arr[end]);
++begin;
--end;
}
}
改进如下:
//避免maxi begini都在同一个位置,begin和mini交换之后,maxi数据变成了最小的数据
if (maxi == begin)
{
maxi = mini;
}完整代码:
void Swap(int* x, int* y)
{
int tmp = *x;
*x = *y;
*y = tmp;
}
void SelectSort(int* arr, int n)
{
int begin = 0;
int end = n - 1;
while (begin < end)
{
int mini = begin, maxi = begin;
for (int i = begin + 1; i <= end; i++)
{
if (arr[i] > arr[maxi])
{
maxi = i;
}
if (arr[i] < arr[mini])
{
mini = i;
}
}
//mini begin
//maxi end
//避免maxi begini都在同一个位置,begin和mini交换之后,maxi数据变成了最小的数据
if (maxi == begin)
{
maxi = mini;
}
Swap(&arr[mini], &arr[begin]);
Swap(&arr[maxi], &arr[end]);
++begin;
--end;
}
}直接选择排序的特性总结:
基本思想
堆排序(Heapsort)是指利⽤堆积树(堆)这种数据结构所设计的⼀种排序算法,它是选择排序的⼀ 种。它是通过堆来进⾏选择数据。需要注意的是排升序要建⼤堆,排降序建⼩堆。
在堆的应用(堆排序与Top-K问题)已经讲过了此方法,这里就不赘述了。
以上就是插入、希尔、选择、堆排序的介绍啦,各位大佬有什么问题欢迎在评论区指正,您的支持是我创作的最大动力!❤️