【排序算法 上】带你手撕常见排序 (插入,希尔,选择,堆排序) (动图详解)

简介: 【排序算法 上】带你手撕常见排序 (插入,希尔,选择,堆排序) (动图详解)

1dedc54107ab559d7f275567213b2dfb_f36e72661804456390e4ccd309137c5d.png

“东风随春归,发我枝上花。”


前言:

排序是日常生活中极其常见的一种算法,它的功能很简单,就是将数字按照升序/降序排列,最终形成一组有序的数字,不过形成有序数字的过程有多种实现方式,它们各有好坏,接下来,由我带你手撕排序算法。


目录

🥰写在前面

💐Part1.插入排序

1.1直接插入排序

1.1.1思想

1.1.2实现

1.2希尔排序

1.2.1思想

1.2.2实现

🌺Part2:选择排序

2.1选择排序

2.1.1思想

2.1.2实现

2.2堆排序

2.2.1思想

2.2.2实现  



写在前面


排序离我们的生活很近,这是一种很重要的算法,比如:

c3c165506fafb724e1a5f6cf480ac41c_1e717b28150547f3bd45f9614a150fb8.png

网上购物按价格升序排序


35df217268fdc67846bef73baa64ff6e_f1ddbaf6db1f47568a43193f672805d7.png

世界500强排名 

排序是常见的,也是重要的,寻找最优的排序能做到优化,所以理解掌握排序是必要的。

下面是要讲解的常见排序:

142f6872bf680b9c13c627d568cd68a2_cc35035667c8430c9a3b6de732ba7a6c.png

我们默认实现排升序,一个一个来:


Part1.插入排序


1.1直接插入排序


1.1.1思想


相信多数人是打牌的,你知道吗,在摸牌的时候,你就悄悄进行了插入排序操作:

d52540e59e2ec89b1374d9de4993d48e_eab17fbc6ac84926a9883d5d4fe01807.png

这种排序就是新拿到的数字与已有的数字进行比较,确定合适的位置后进行插入操作。


结合动图深度理解:

b851013075cd1469d180ade907e64bdd_7c888505e39c45b780f28f4bfb8f61be.gif


可以看到:

• 当只有一个数字时,没有可比性,可理解为有序;

•  取下一个数字b,与前一个数字a比较,如果b小于a,则需要调整二者的位置,如果a小于b,符合升序,则不需要调整;

•  前一部分排好的数字逐渐增多,取此区间后最近的数字进行挨个比对,不是合适位置,比较过的数字就后移一位,是合适位置,就进行插入操作。


1.1.2实现

// 插入排序
void InsertSort(int* a, int n)
{
  for (int i = 1; i < n; i++)
  {
    int tmp= a[i];
    int endi = i - 1;
    while (endi >= 0)
    {
      if (tmp < a[endi])
      {
        a[endi + 1] = a[endi];
        endi--;
      }
      else
        break;
      a[endi + 1] = tmp;
    }
  }
}


特征分析:

当原数字越接近有序时,效率越高;

时间复杂度:最好:O(N)  最坏:O(N^2);

空间复杂度:O(1);

稳定性(是否动了相同数字的相对位置)稳定

这个排序看起来效率并不高,希尔在快速排序的基础上进行了优化:

1.2希尔排序


1.2.1思想


本质还是插入排序,只不过希尔做了一个巧妙的处理:引入了 gap ,每隔一个gap分为一组;

先让一部分数字相对有序,再调整下一部分,直至最后有序;

4a92833481a503331301d030c26df65d_404b2b98640f4f8babed2a54e28c031e.png

1.2.2实现


// 希尔排序
void ShellSort(int* a, int n)
{
  int gap = n;
  while (gap > 1)
  {
    //gap /= 2;
    gap = gap/3 + 1;
    for (int i = 0; i < n - gap; i++)
    {
      int endi = i;
      int tmp = a[i + gap];
      while (endi >= 0)
      {
        if (tmp < a[endi])
        {
          a[endi + gap] = a[endi];
          endi -= gap;
        }
        else
          break;
        a[endi + gap] = tmp;
      }
    }
  }
}


注意:gap是多少并没有明确规定,一般是 gap/3+1  

特征分析

希尔排序是对直接插入排序的优化,给gap相当于进行预排序,当gap==1时数组就相当有序了,比起直接插入,会快一些;

时间复杂度:最好:O(N^1.25)~O(N^1.3) (参考《计算机程序设计技巧》--Knuth)  最坏:O(N^2);

空间复杂度:O(1);

稳定性:不稳定


Part2:选择排序


2.1选择排序


2.1.1思想


这种排序方法是我们出厂自带的排序方法,试想:给你一堆乱序的数字,你会怎么排?

通常情况下,我们会从头到尾扫一遍,选出最小的放到最前面,再扫一眼,选出第二大的放到第二位。

这就是选择排序的基本思想。


动图: 

7fd1ef1fda2919f310839dbd6ce53ff6_933fe2d54a4f4b96a25a38d99c354b9d.gif

是不是挺符合认知规律的?


2.1.2实现


// 选择排序
void SelectSort(int* a, int n)
{
  int left = 0, right = n - 1;
  while (left < right)
  {
        // 初始化
    int mini = left;
    int maxi = left;
        // 找到较大/较小值的下标
    for (int i = left+1; i <= right; i++)
    {
      if (a[i] < a[mini]) // 前后顺序会影响结果
      {
        mini = i;
      }
      if (a[i] > a[maxi])
      {
        maxi = i;
      }
    }
    Swap(&a[mini], &a[left]);
        // 调试过程中会有left与maxi重叠的情况,这时需要针对这种情况调整
    if (left == maxi)
      maxi = mini;
    Swap(&a[maxi], &a[right]);
    left++;
    right--;
  }
}


特征分析:

直接选择排序思路易理解,但效率不高,不实用;

时间复杂度:最好:O(N^2)  最坏:O(N^2);

空间复杂度:O(1);

稳定性:不稳定


2.2堆排序


2.2.1思想


堆排序在往期 什么是堆,如何实现?(附堆排序,TOP-K问题) 中有详解,

先是建大堆,再是模拟删除操作,这里就不多说啦。


2.2.2实现


//堆排序
void HeapSort(int* a, int n)
{
  //向下调整(效率更高)
  for (int i = (n - 1 - 1) / 2; i >= 0; i--)
  {
    AdjustDown(a, n, i);
  }
  int end = n - 1;
  while (end > 0)
  {
    Swap(&a[0], &a[end]);
    AdjustDown(a, end, 0);
    end--;
  }
}


特征分析:

利用堆的特性,选数快很多,效率较高

时间复杂度:最坏:O(N*logN)  最好:O(N*logN) ;

空间复杂度:O(1);

稳定性:不稳定


总结:

这期是常见排序的前半部分,讲了两类排序:插入排序和选择排序,思想不难,多注意实现中的细节。我打算将后半部分放在下期:交换排序和归并排序。

目录
相关文章
|
1月前
|
搜索推荐 算法 C语言
【排序算法】八大排序(上)(c语言实现)(附源码)
本文介绍了四种常见的排序算法:冒泡排序、选择排序、插入排序和希尔排序。通过具体的代码实现和测试数据,详细解释了每种算法的工作原理和性能特点。冒泡排序通过不断交换相邻元素来排序,选择排序通过选择最小元素进行交换,插入排序通过逐步插入元素到已排序部分,而希尔排序则是插入排序的改进版,通过预排序使数据更接近有序,从而提高效率。文章最后总结了这四种算法的空间和时间复杂度,以及它们的稳定性。
95 8
|
1月前
|
搜索推荐 算法 C语言
【排序算法】八大排序(下)(c语言实现)(附源码)
本文继续学习并实现了八大排序算法中的后四种:堆排序、快速排序、归并排序和计数排序。详细介绍了每种排序算法的原理、步骤和代码实现,并通过测试数据展示了它们的性能表现。堆排序利用堆的特性进行排序,快速排序通过递归和多种划分方法实现高效排序,归并排序通过分治法将问题分解后再合并,计数排序则通过统计每个元素的出现次数实现非比较排序。最后,文章还对比了这些排序算法在处理一百万个整形数据时的运行时间,帮助读者了解不同算法的优劣。
109 7
|
2月前
|
搜索推荐 Shell
解析排序算法:十大排序方法的工作原理与性能比较
解析排序算法:十大排序方法的工作原理与性能比较
73 9
|
2月前
|
算法 搜索推荐
数据结构与算法学习十八:堆排序
这篇文章介绍了堆排序是一种通过构建堆数据结构来实现的高效排序算法,具有平均和最坏时间复杂度为O(nlogn)的特点。
75 0
数据结构与算法学习十八:堆排序
|
2月前
|
算法 搜索推荐 Java
数据结构与算法学习十三:基数排序,以空间换时间的稳定式排序,速度很快。
基数排序是一种稳定的排序算法,通过将数字按位数切割并分配到不同的桶中,以空间换时间的方式实现快速排序,但占用内存较大,不适合含有负数的数组。
38 0
数据结构与算法学习十三:基数排序,以空间换时间的稳定式排序,速度很快。
|
2月前
|
算法 搜索推荐
算法之堆排序
本文介绍了堆排序算法的原理和实现,通过构建最大堆或最小堆,利用堆的性质进行高效的排序,并提供了具体的编程实现细节和示例。
29 0
算法之堆排序
|
2月前
|
算法 Java Go
深入了解堆排序算法
深入了解堆排序算法
39 1
|
2月前
|
算法
❤️算法笔记❤️-(每日一刷-83、删除排序链表中的重复项)
❤️算法笔记❤️-(每日一刷-83、删除排序链表中的重复项)
34 0
|
2月前
|
存储 算法 搜索推荐
算法进阶之路:Python 归并排序深度剖析,让数据排序变得艺术起来!
算法进阶之路:Python 归并排序深度剖析,让数据排序变得艺术起来!
79 0
|
17天前
|
算法
基于WOA算法的SVDD参数寻优matlab仿真
该程序利用鲸鱼优化算法(WOA)对支持向量数据描述(SVDD)模型的参数进行优化,以提高数据分类的准确性。通过MATLAB2022A实现,展示了不同信噪比(SNR)下模型的分类误差。WOA通过模拟鲸鱼捕食行为,动态调整SVDD参数,如惩罚因子C和核函数参数γ,以寻找最优参数组合,增强模型的鲁棒性和泛化能力。
下一篇
DataWorks