没有哪一个排序方法是完美的,对于不同的需求,排序算法各有自己的优势。金无足赤,人无完人。
(这里不再重复所讲排序算法的实现,网上已有很多好的教学。)
排序方法除了依靠时间复杂度和空间复杂度来区分,排序算法还有内部和外部之分、稳定与非稳定之分、交互与非交互之分,其中时间和空间复杂度还受储存方式的影响(如顺序表、链表等),比较次数与初态也有关系等等。
首先我们来介绍一下内部排序和外部排序:
内部排序和外部排序
内部排序和外部排序的主要区别在于数据存储位置和数据量大小,在实际应用中,选择哪种排序方式取决于数据量的大小和硬件资源的限制。
内部排序是指所有待排序的数据可以一次性加载到内存中进行排序。适用于数据量较小、内存足够容纳全部数据的场景。外部排序是指待排序的数据量太大,无法一次性加载到内存中,需要借助外部存储(如磁盘)进行排序。适用于数据量远大于内存容量的场景。
常见的排序算法如冒泡排序(Bubble Sort)、选择排序(Selection Sort)、插入排序(Insertion Sort)、快速排序(Quick Sort)、归并排序(Merge Sort)、堆排序(Heap Sort)、希尔排序(Shell Sort)都是内部排序。
稳定与非稳定性
根据排序前后相同关键字的相对位置是否发生变化,把排序分为稳定和不稳定两类。
多关键字排序 即需要根据多个条件进行排序时,排序时先按第一个关键字排序,如果第一个关键字相同,再按第二个关键字排序,以此类推。。稳定排序保证在第二关键字排序时,第一关键字的顺序不会被破坏。
假设有以下学生数据:
我们要求,先按分数进行降序排序,如果分数相同,再按“年龄”升序排序。
-
按“分数”降序排序:
-
李四(90分)、赵六(90分)、张三(85分)、王五(85分)。
-
-
对分数相同的记录,按“年龄”升序排序:
-
李四(22)、赵六(21)、张三(20)、王五(20)。
-
在数据库查询中,先按“姓名”排序,再按“年龄”排序。如果排序算法不稳定,可能会导致相同年龄的记录中,姓名的顺序混乱。同样在事件驱动系统中,事件可能需要按时间戳排序,同时保持相同时间戳的事件的原始顺序等,排序算法的稳定性也有很大的影响。
交互与非交互性
交互排序是指在排序过程中需要用户参与或实时交互的排序方式。排序的每一步都可能依赖于用户的输入或反馈。非交互排序就是指在排序过程中不需要用户参与,排序算法独立完成所有操作。排序结果在排序过程结束后一次性输出。
显然,交互排序需求更多,复杂度也就更高,在需要用户反馈、评分的推荐系统、或者是交互式数据可视化、调查或投票等,交互排序肯定更好;但在批量数据处理、文件排序、科学计算或者离线数据分析这些不需要用户参与的排序中,非交互排序更好。
内部排序方法
我们这里主要介绍一下内部排序方法, 根据排列所依据的策略不同,排序可以分为插入、选择、交换、分配和归并方法。
插入类
插入排序(Insertion Sort)的基本思想是将数组分为已排序部分和未排序部分,逐个将未排序部分的元素插入到已排序部分的正确位置。基于这个思想,如果初态基本有序,那么比较次数就会越少。
可以选择不同的方式在已排好的记录中寻找插入位置,根据查找方式不同,有多种插入排序方法,如直接插入排序、折半插入排序、希尔排序。
选择类
选择排序(Selection Sort)基本思想是每次从未排序部分选择最小(或最大)的元素,放到已排序部分的末尾。
简单选择排序和堆排序就是选择类。
归并类
归并排序(Merge Sort)基本思想是采用分治法,将数组分成两半分别排序,然后将排序后的两部分合并。其中2-路归并排序和折半查找法像是一个思想的相反应用。
交换类
交换排序(Exchange Sort)基本思想是通过交换相邻元素的位置来排序,典型的例子是冒泡排序。我们熟知的冒泡排序和快速排序就是交换类。
分配类
前述各类排序方法都建立在关键字比较的基础上,而分配类排序不需要比较关键字的大小,它是根据关键字中各位的值,通过对待排序记录进行若干躺“分配”与“收集”来实现排序的,是一种借助于多关键字排序的思想对但关键字进行排序的方法。其中计数排序、桶排序和基数排序是典型的分配类排序。
总结
正如标题所说,每个排序算法都各有优势,我们没办法直接说那个算法最好,针对与不同的场景,用到的算法也不同,金无赤足,人无完人,排序算法也一样。