当前位置:   article > 正文

【插入排序】手撕八大排序之直接插入排序和希尔排序

【插入排序】手撕八大排序之直接插入排序和希尔排序

目录

前言:

一.插入排序

1.直接插入排序

2.希尔排序 


前言:

排序一共有八种:冒泡排序、选择排序、插入排序、希尔排序、归并排序、快速排序、堆排序和计数排序这八种排序算法。

排序在我们现实生活中其实是非常常见的,而且很多地方的分不开它们,当我们在某软件网购的时候,我们可以使用价格排序,销量排序,好评排序来筛选我们想要的商品。

这里我选择销量和价格两个条件,软件就会自动的给顾客排好序,这底层的逻辑就是使用的排序算法,可知排序算法是非常重要的,只不过现在学习的八大排序是很基础的,后面的红黑树和AV树才是真正的巨头,现在的我们必须要打牢基础,脚踏实地的学习。 

一.插入排序

1.直接插入排序

插入排序顾名思义,就是一个一个的插入,如同我们摸扑克牌一样,当摸第一张牌上来的时候,直接放到手里面即可,摸第二张的时候,就和手里面的比较,看是放在后面还是前面。依次类推,每次摸牌起来的时候,手里的牌都是有序的,只需要把这张牌插入到有序的牌中即可。

注意:这里我们的排序都是排的升序。 

  1. //直接插入排序
  2. void InsertSort(int* a, int n)
  3. {
  4. for (int i = 1; i < n; i++)
  5. {
  6. int end = i - 1;//这是有序数组的最后一个元素
  7. int temp = a[i];
  8. while (end >= 0)
  9. {
  10. if (temp < a[end])//待插入的数和最后这个数进行比较
  11. {
  12. a[end+1]=a[end];
  13. end--;//交换了,end就往前移一位,再继续进行比较
  14. }
  15. else
  16. {
  17. break;
  18. }
  19. }
  20. a[end + 1] = temp;
  21. }
  22. }

这里动图不太理解,我们还是一样画图来理解。
第一次:
第二次: 

第三次: 

这时已经退出while循环了,但是数组中还有一位是空缺的,并且end已经不指向数组的位置了,所以最后一句的a[end+1]=temp,就是为了把这个位置给填上。 
就这样不断简单的重复,就可以把这个无序的数组排序为一个升序的数组。

直接插入排序的特性总结:
1. 元素集合越接近有序,直接插入排序算法的时间效率越高
2. 时间复杂度: O(N^2)
3. 空间复杂度: O(1) ,它是一种稳定的排序算法
4. 稳定性:稳定

2.希尔排序 

希尔排序是由美国计算机科学家 Donald Shell 在1959年提出的。当时,他发表了一篇名为 “A High-Speed Sorting Procedure” 的论文,介绍了这种排序算法。

希尔排序本质上是插入排序的一种改进版本。传统的插入排序在排序一个逆序的序列时效率较低,因为它每次只能将一个元素移动一个位置。为了解决这个问题,Shell 提出了一种逐步缩小增量的方法,即先比较距离较远的元素,然后逐渐缩小间隔,最终达到相邻元素之间的比较。
希尔排序的核心概念是使用一个增量序列,将待排序的序列分割成多个子序列,对每个子序列进行插入排序。然后逐渐缩小增量,直至增量为1,进行最后一次插入排序。通过这种方式,希尔排序能够高效地排序序列。

最后增量为1时,也就变成了直接插入排序了。 

动画演示:

第一次设置间隔为5,那么49和13比较然后进行排序,38和27进行比较然后进行排序。依次类推,直到把所有间隔为5的数先排序完。
第二次就把间隔缩小,继续排序,直到间隔为1了之后,再次排序就成立直接插入排序,这样排序了之后还数组的数据就变成有序的了。
但是这里有一个点需要我们注意,那就是如何将间隔逐渐缩小,到最后为1,这就需要对数组的大小做文章了。

  1. //希尔排序
  2. void ShellSort(int* a, int n)
  3. {
  4. int gap = n;
  5. while (gap > 1)//gap等于1,那么就排序完毕了,即退出循环
  6. {
  7. gap = gap / 3 + 1;
  8. //不管n是偶数还是奇数,只要最后+1,最后的gap一定会变成1
  9. for (int i = 0; i < n - gap; i += gap)
  10. //注意这里i要小于n-gap,不然下面的temp可能就会越界
  11. {
  12. int end = i;
  13. int temp = a[i + gap];
  14. while (end >= 0)
  15. //注意下面的逻辑和直接插入排序的逻辑是一样的,只是间隔不一样罢了
  16. {
  17. if (temp < a[end])
  18. {
  19. Swap(&a[end + gap], &a[end]);
  20. end -= gap;
  21. }
  22. else
  23. {
  24. break;
  25. }
  26. }
  27. a[end + gap] = temp;
  28. }
  29. }
  30. }

希尔排序的特性总结:
1. 希尔排序是对直接插入排序的优化。
2. gap > 1 时都是预排序,目的是让数组更接近于有序。当 gap == 1 时,数组已经接近有序的了,这样就会很快。这样整体而言,可以达到优化的效果。我们实现后可以进行性能测试的对比。
3. 希尔排序的时间复杂度不好计算,因为 gap的取值方法很多,导致很难去计算,因此在好些书中给出的希尔排序的时间复杂度都不固定。
我们暂时就记为:希尔排序的时间复杂度为O(n^(2/3))。
4.稳定性:不稳定。

声明:本文内容由网友自发贡献,不代表【wpsshop博客】立场,版权归原作者所有,本站不承担相应法律责任。如您发现有侵权的内容,请联系我们。转载请注明出处:https://www.wpsshop.cn/w/AllinToyou/article/detail/695237
推荐阅读
相关标签
  

闽ICP备14008679号