• 插入排序、希尔排序


    目录

    插入排序

    希尔排序


    插入排序

     插入排序就像我们打扑克摸牌理顺的过程一样,将后面的牌一张一张按大小(升降序)排列好。

     插入排序也就是这么个过程,这里假设排升序,步骤:

    1、先实现单次的插入过程,先取末尾的数(先不初始化值),下标记为end,找他后面一个数(下标为end+1),用一个临时变量tmp记录,比较这两个数,如果前面>后面,就把前面的数往后挪一位,--end。

    2、重复上述过程,直到end < 0 为止,显然这是一个循环的过程,条件就是end >= 0,如果前面 < 后面,就break出来。这时再将tmp放到end+1的位置就完成了单次的插排。

    3、单次的完成,在外面套一个循环就可以实现整个插入排序了,这时也能确定end的初始值。

     

    代码如下:

    1. void Insertsort(int* a, int n)
    2. {
    3. //将下标为end+1的数 插入到[0,end]里面,并保证有序
    4. for (int i = 0; i < n - 1; ++i)
    5. {
    6. int end = i;
    7. int tmp = a[end + 1];
    8. while (end >= 0)
    9. {
    10. if (a[end] > tmp)
    11. {
    12. a[end + 1] = a[end];
    13. --end;
    14. }
    15. else
    16. break;
    17. }
    18. a[end + 1] = tmp;
    19. }
    20. }
    21. int main()
    22. {
    23. int a[] = { 6,1,4,65,11,15,46,23,60,12,9 };
    24. int sz = sizeof(a) / sizeof(a[0]);
    25. Insertsort(a, sz);
    26. for (int i = 0; i < sz; ++i)
    27. {
    28. printf("%d ", a[i]);
    29. }
    30. return 0;
    31. }

    希尔排序

    希尔排序实质上就是插入排序的深度优化,但是它的效率一下就提升到和堆排这种时间复杂度为O(N*logN)差不多的级别了。

    那希尔排序是如何实现优化的呢?实际上它在插入排序之前进行了预排序,目的是让序列更接近有序。

     这里定义一个gap,假设gap = 3,将降序序列{9,8,7,6,5,4,3,2,1,0} 按间隔为gap的 为一组进行插入排序,如9,6,3,0为一组,8,5,2为一组,7,4,1为一组,分成这样的三组插入排序,排完后序列变成{0,2,1,3,5,4,6,8,7,9} 将小的基本放在了前面,将大的基本放在了后面,实现了基本的有序,这就为最后一次的排序做了铺垫,这就是预排序。

    很多人会问,预排序不也是插入排序吗,为什么进行多次预排序反而比一次直接插入排序效率高呢?这就是gap的妙处了,如果是插入排序,需要从头到尾依次挪动数据,如果不巧可能最后一个数要一直往前插入到最前面。   先进行预排序的话,能先大致处理序列的有序性,基本避免了最坏的情况,能节省很多时间。

    我们来尝试实现一下希尔排序

    还是和插入排序一样,先实现单次的排序

    1. int end;
    2. int tmp = a[end + gap];
    3. while (end >= 0)
    4. {
    5. if (a[end] > tmp)
    6. {
    7. a[end + gap] = a[end];
    8. end -= gap;
    9. }
    10. else
    11. break;
    12. }
    13. a[end + gap] = tmp;

    单次的希尔排序就完成了,实际上就是插入排序就是gap = 1 的情况

    来看整体的:

    预排后就是gap组多组并排,这里有两种写法:

    一、  三层循环

    1. int gap = 3;
    2. for (int j = 0; j < gap; ++j)
    3. {
    4. for (int i = j; i < n - gap; i+= gap)
    5. {
    6. int end = i;
    7. int tmp = a[end + gap];
    8. while (end >= 0)
    9. {
    10. if (a[end] > tmp)
    11. {
    12. a[end + gap] = a[end];
    13. end -= gap;
    14. }
    15. else
    16. break;
    17. }
    18. a[end + gap] = tmp;
    19. }
    20. }
    21. }

    这种就是按上面的图一组一组分别排序每个gap组的数据。

    先排9,6,3,0这一组,再排8,5,2这一组,再排7,4,1这一组。分别排序,排完一组再排下一组。

    二、两层循环

    1. int gap = 3;
    2. for (int i = 0; i < n - gap; i++)
    3. {
    4. int end = i;
    5. int tmp = a[end + gap];
    6. while (end >= 0)
    7. {
    8. if (a[end] > tmp)
    9. {
    10. a[end + gap] = a[end];
    11. end -= gap;
    12. }
    13. else
    14. break;
    15. }
    16. a[end + gap] = tmp;
    17. }
    18. }

    这种是改进过的,看上去更简便一些。与上面一种排序顺序不同,它是依次顺着序列往下走的。

    先排第一个数,9,6排一下; 再排第二个数,8,5排一下; 再排第三个数,7,4排一下。

    它是依次往下排的,结果来说也是一样的。

    最外层:到这 希尔排序还差最外层调整gap

    按上面情况分析,gap越大,跳的越快,但是相对可能没那么有序。

                                 gap越小,跳的越慢,但是更接近有序。

    所以说,gap = 1 时也就是直接插入排序了。

    因此,我们用数组个数n来控制gap,让它实现多组排序。

    1. void Shellsort(int* a, int n)
    2. {
    3. int gap = n;
    4. while (gap > 1)
    5. {
    6. gap = gap / 3 + 1;
    7. for (int i = 0; i < n - gap; i++)
    8. {
    9. int end = i;
    10. int tmp = a[end + gap];
    11. //将下标为end+gap的数 插入到[0,end]里面,并保证有序
    12. while (end >= 0)
    13. {
    14. if (a[end] > tmp)
    15. {
    16. a[end + gap] = a[end];
    17. end -= gap;
    18. }
    19. else
    20. break;
    21. }
    22. a[end + gap] = tmp;
    23. }
    24. }
    25. }

    当gap > 1时,就是预排序,当gap = 1时,进行最后一次直接插入排序。

    这里gap / 3+1 是为了保证gap最后一次 == 1进入,当然gap / 2也是可以的,但是经过测试还是/3效率更高一点。

    希尔排序时间复杂度大概是O(N^1.3),这是个大概的测试估值,算是比较官方的数据了,要计算它的时间复杂度需要很深的数学功底了。

  • 相关阅读:
    改变工作目录和根目录
    外汇天眼:如何有效地交易外汇?15个基本提示!
    国产化Kettle、JDK、MySQL下载安装操作步骤
    关于 分布式事务 你知道多少
    软件测试 -- 进阶 5 软件测试用例
    200.岛屿数量
    ChatGPT AIGC总结Excel中Vlookup,lookup,xlookup的区别
    支持百万并发的Web端即时通讯方案
    3.17 haas506 2.0开发教程-example - 低功耗模式 (2.2版本接口有更新)
    腾讯云EdgeOne对比普通CDN的分别
  • 原文地址:https://blog.csdn.net/SAKURAjinx/article/details/126685210