Bootstrap

十大经典排序算法全解析

一、算法分类与复杂度

比较类排序:通过比较来决定元素间的相对次序,由于其时间复杂度不能突破 O (nlogn),因此也称为非线性时间比较类排序。这类排序算法包括冒泡排序、选择排序、插入排序、希尔排序、归并排序、快速排序等。

  • 冒泡排序:时间复杂度平均为 O (n²),最坏情况也是 O (n²),最好情况为 O (n)。空间复杂度为 O (1)。它重复地走访要排序的数列,一次比较两个元素,如果顺序错误就交换过来,直到没有再需要交换,数列排序完成。
  • 选择排序:时间复杂度固定为 O (n²)。空间复杂度为 O (1)。在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,再从剩余未排序元素中继续找最小(大)元素,放到已排序序列末尾,直到所有元素排序完毕。
  • 插入排序:平均时间复杂度为 O (n²),最好情况为 O (n),最坏情况为 O (n²)。空间复杂度为 O (1)。通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。
  • 希尔排序:时间复杂度基于不同增量选择为 O (n1.3~n²)。空间复杂度为 O (1)。是插入排序的改进版,先将待排序记录序列分割成若干子序列分别进行直接插入排序。

非比较类排序:不通过比较来决定元素间的相对次序,它可以突破基于比较排序的时间下界,以线性时间运行,因此也称为线性时间非比较类排序。包括计数排序、基数排序、桶排序等。

  • 计数排序:当对一定范围内的整数排序时,复杂度为 Ο(n+k)(其中 k 是整数的范围)。是一个非基于比较的排序算法,以空间换时间。
  • 基数排序:时间复杂度为 O (d * (n + k)),其中 d 是位数,n 是元素个数,k 是进制基数。通过对数字的各个位数进行分别排序来实现整体排序。
  • 桶排序:时间复杂度通常为 O (n + k),其中 n 是元素个数,k 是桶的个数。把数据分到有限数量的桶里,对每个桶分别排序再将结果合并。

        相关概念包括稳定性等。稳定性是指如果待排序的记录序列中存在多个具有相同关键字的记录,经过排序后,这些记录的相对次序保持不变。稳定的排序算法有冒泡排序、插入排序、归并排序和基数排序;不稳定的排序算法有选择排序、快速排序、希尔排序、堆排序。复杂度主要从算法所占用的「时间」和「空间」两个维度去考量。时间维度用「时间复杂度」描述,空间维度用「空间复杂度」描述。

        各个排序算法的时间复杂度、空间复杂度及稳定性如下:

二、具体算法详解

(一)冒泡排序

算法描述:比较相邻的元素。如果第一个比第二个大,就交换它们两个。对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数。针对所有的元素重复以上的步骤,直到排序完成。

算法分析:最佳情况是当输入的数组已经是有序的,此时只需要进行一次遍历,时间复杂度为 O (n)。最差情况是当输入的数组是逆序的,需要进行 n*(n-1)/2 次比较和交换,时间复杂度为 O (n²)。平均情况的时间复杂度也为 O (n²)。空间复杂度为 O (1),因为只需要几个额外的变量来进行交换操作,不需要额外的存储空间。

详细代码及分析请看此文:基于C语言的冒泡排序算法_c语言实现冒泡算法-CSDN博客

(二)选择排序

算法步骤:首先在未排序序列中找到最小(大)元素,存放到排序序列的起始位置,再从剩余未排序元素中继续寻找最小(大)元素,放到已排序序列的末尾,直到所有元素均排序完毕。

算法分析:选择排序是一种不稳定的排序算法,无论什么数据进去都是 O (n²) 的时间复杂度。空间复杂度为 O (1),不占用额外的内存空间。

详细代码及分析请看此文:基于C语言的选择排序算法-CSDN博客

(三)插入排序

工作原理:通过构建有序序列,对于未排序数据,在已排序序列中从后向前扫描,找到相应位置并插入。

分析其时间和空间复杂度:平均时间复杂度为 O (n²),最好情况为输入的数组已经是有序的,时间复杂度为 O (n),最坏情况为输入的数组是逆序的,时间复杂度为 O (n²)。空间复杂度为 O (1)。

适用场景:适用于小规模数据或基本有序的数据。

详细代码及分析请看此文:基于C语言的插入排序算法-CSDN博客

(四)希尔排序

原理:希尔排序是插入排序的改进版,先将待排序记录序列分割成若干子序列分别进行直接插入排序,然后逐步减小增量,最终进行一次普通的插入排序。

实现步骤

  1. 选择增量序列,常见的增量序列有希尔序列、Hibbard 序列、Sedgewick 序列等。
  2. 对于选定的每个增量 d,将待排序序列按照 d 的间距划分为多个子序列,对每个子序列进行插入排序。
  3. 重复步骤 2,每次减小增量,直至增量为 1,此时进行最后一次插入排序,得到最终有序序列。

分析其时间和空间复杂度及稳定性:时间复杂度取决于所选增量序列,在理想情况下,当增量序列选择得当,时间复杂度可以达到接近 O (n^(3/2)),远优于普通插入排序的 O (n²)。然而,对于某些增量序列,时间复杂度可能退化为 O (n²)。空间复杂度为 O (1)。希尔排序是不稳定的排序算法,即相等元素的相对顺序在排序过程中可能会改变。

详细代码及分析请看此文:基于C语言的希尔排序算法-CSDN博客

(五)堆排序

过程:堆排序首先将待排序的数组构建成一个大顶堆。然后,将堆顶元素(最大元素)与堆的最后一个元素交换,此时最大元素位于数组的末尾。接着,对剩余的元素重新构建大顶堆,重复这个过程,直到整个数组有序。

分析:时间复杂度为 O (nlogn)。空间复杂度为 O (1),因为只需要在原数组上进行操作,不需要额外的存储空间。堆排序是不稳定的排序算法。

详细代码及分析请看此文:基于C语言的堆排序算法-CSDN博客

(六)快速排序

原理:通过一趟排序算法把所需要排序的序列的元素分割成两大块,其中,一部分的元素都要小于或等于另外一部分的序列元素,然后仍根据该种方法对划分后的这两块序列的元素分别再次实行快速排序算法,排序实现的整个过程可以是递归的来进行调用,最终能够实现将所需排序的无序序列元素变为一个有序的序列。

实现步骤

  1. 从数列中取出一个数作为基准数。
  2. 分区,将比它大的数全放到它的右边,小于或等于它的数全放到它的左边。
  3. 再对左右区间重复第二步,直到各区间只有一个数。

时间与空间复杂度、特点优缺点及应用场景:时间复杂度平均为 O (nlogn),最坏情况为 O (n²)。空间复杂度平均为 O (logn),最坏情况为 O (n)。快速排序是一种不稳定的排序算法。特点是速度快,适用于大规模数据的排序。缺点是在最坏情况下时间复杂度较高。应用场景广泛,特别是对于大规模数据的排序。

详细代码及分析请看此文:基于C语言的快速排序算法-CSDN博客

(七)归并排序

原理:归并排序算法就是把序列递归划分成为一个个短序列,以其中只有 1 个元素的直接序列或者只有 2 个元素的序列作为短序列的递归出口,再将全部有序的短序列按照一定的规则进行排序为长序列。

步骤

  1. 将待排序序列递归地分成短序列,直到短序列只有一个或两个元素。
  2. 对两个有序的短序列进行合并,将较小的元素依次放入新的序列中,直到两个短序列都合并完成。
  3. 重复步骤 2,将合并后的长序列继续与其他长序列合并,直到整个序列有序。

复杂度分析:时间复杂度为 O (nlogn),空间复杂度为 O (n),因为在合并过程中需要额外的空间来存储临时序列。归并排序是一种稳定的排序算法。

详细代码及分析请看此文:基于C语言的归并排序算法_归并排序c算法实现-CSDN博客

(八)计数排序

特点:计数排序是一种非基于比较的排序算法,以空间换时间。它适用于一定范围内的整数排序。

适用范围:当对一定范围内的整数排序时效果较好,特别是当整数范围较小且数据量大时。

实现方法:首先统计每个整数出现的次数,然后根据统计结果依次输出每个整数。

复杂度:时间复杂度为 Ο(n+k)(其中 k 是整数的范围),空间复杂度也为 Ο(n+k)。

详细代码及分析请看此文:基于C语言的计数排序算法-CSDN博客

(九)基数排序

原理:基数排序通过对数字的各个位数进行分别排序来实现整体排序。首先对最低位进行排序,然后对次低位进行排序,依次类推,直到对最高位排序完成。

过程:将待排序的数字按照位数分成多个桶,对每个桶中的数字进行排序,然后按照桶的顺序依次取出数字,组成新的有序序列。

优势:可以处理大规模数据,时间复杂度较低。

复杂度:时间复杂度为 O (d * (n + k)),其中 d 是位数,n 是元素个数,k 是进制基数。空间复杂度为 O (n + k)。

详细代码及分析请看此文:基于C语言的基数排序算法-CSDN博客

(十)桶排序

概念:桶排序把数据分到有限数量的桶里,对每个桶分别排序再将结果合并。

实现方式:首先确定桶的数量和每个桶的范围,然后将数据分配到各个桶中,对每个桶进行排序,可以使用其他排序算法,最后将各个桶中的数据依次取出,组成有序序列。

适用情况:适用于数据分布比较均匀的情况。

复杂度:时间复杂度通常为 O (n + k),其中 n 是元素个数,k 是桶的个数。空间复杂度取决于桶的数量和数据的分布情况。

详细代码及分析请看此文:基于C语言的桶排序算法-CSDN博客

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;