目录
前言:
排序一共有八种:冒泡排序、选择排序、插入排序、希尔排序、归并排序、快速排序、堆排序和计数排序这八种排序算法。
排序在我们现实生活中其实是非常常见的,而且很多地方的分不开它们,当我们在某软件网购的时候,我们可以使用价格排序,销量排序,好评排序来筛选我们想要的商品。
这里我选择销量和价格两个条件,软件就会自动的给顾客排好序,这底层的逻辑就是使用的排序算法,可知排序算法是非常重要的,只不过现在学习的八大排序是很基础的,后面的红黑树和AV树才是真正的巨头,现在的我们必须要打牢基础,脚踏实地的学习。
一.插入排序
1.直接插入排序
插入排序顾名思义,就是一个一个的插入,如同我们摸扑克牌一样,当摸第一张牌上来的时候,直接放到手里面即可,摸第二张的时候,就和手里面的比较,看是放在后面还是前面。依次类推,每次摸牌起来的时候,手里的牌都是有序的,只需要把这张牌插入到有序的牌中即可。
注意:这里我们的排序都是排的升序。
//直接插入排序
void InsertSort(int* a, int n)
{
for (int i = 1; i < n; i++)
{
int end = i - 1;//这是有序数组的最后一个元素
int temp = a[i];
while (end >= 0)
{
if (temp < a[end])//待插入的数和最后这个数进行比较
{
a[end+1]=a[end];
end--;//交换了,end就往前移一位,再继续进行比较
}
else
{
break;
}
}
a[end + 1] = temp;
}
}
这里动图不太理解,我们还是一样画图来理解。
第一次:
第二次:
第三次:
这时已经退出while循环了,但是数组中还有一位是空缺的,并且end已经不指向数组的位置了,所以最后一句的a[end+1]=temp,就是为了把这个位置给填上。
就这样不断简单的重复,就可以把这个无序的数组排序为一个升序的数组。
2.希尔排序
希尔排序是由美国计算机科学家 Donald Shell 在1959年提出的。当时,他发表了一篇名为 “A High-Speed Sorting Procedure” 的论文,介绍了这种排序算法。
希尔排序本质上是插入排序的一种改进版本。传统的插入排序在排序一个逆序的序列时效率较低,因为它每次只能将一个元素移动一个位置。为了解决这个问题,Shell 提出了一种逐步缩小增量的方法,即先比较距离较远的元素,然后逐渐缩小间隔,最终达到相邻元素之间的比较。
希尔排序的核心概念是使用一个增量序列,将待排序的序列分割成多个子序列,对每个子序列进行插入排序。然后逐渐缩小增量,直至增量为1,进行最后一次插入排序。通过这种方式,希尔排序能够高效地排序序列。
最后增量为1时,也就变成了直接插入排序了。
动画演示:
第一次设置间隔为5,那么49和13比较然后进行排序,38和27进行比较然后进行排序。依次类推,直到把所有间隔为5的数先排序完。
第二次就把间隔缩小,继续排序,直到间隔为1了之后,再次排序就成立直接插入排序,这样排序了之后还数组的数据就变成有序的了。
但是这里有一个点需要我们注意,那就是如何将间隔逐渐缩小,到最后为1,这就需要对数组的大小做文章了。
//希尔排序
void ShellSort(int* a, int n)
{
int gap = n;
while (gap > 1)//gap等于1,那么就排序完毕了,即退出循环
{
gap = gap / 3 + 1;
//不管n是偶数还是奇数,只要最后+1,最后的gap一定会变成1
for (int i = 0; i < n - gap; i += gap)
//注意这里i要小于n-gap,不然下面的temp可能就会越界
{
int end = i;
int temp = a[i + gap];
while (end >= 0)
//注意下面的逻辑和直接插入排序的逻辑是一样的,只是间隔不一样罢了
{
if (temp < a[end])
{
Swap(&a[end + gap], &a[end]);
end -= gap;
}
else
{
break;
}
}
a[end + gap] = temp;
}
}
}
我们暂时就记为:希尔排序的时间复杂度为O(n^(2/3))。