排序算法稳定性
各种排序对比实验报告

一、实验目的通过对不同排序算法的实验对比,分析各种排序算法的效率、稳定性和适用场景,为实际应用中选择合适的排序算法提供参考。
二、实验环境1. 操作系统:Windows 102. 编程语言:Python3.83. 测试数据:随机生成长度为10000的整数数组三、实验方法1. 实验数据:随机生成长度为10000的整数数组,作为排序算法的输入数据。
2. 实验算法:选择以下排序算法进行对比实验:- 冒泡排序(Bubble Sort)- 选择排序(Selection Sort)- 插入排序(Insertion Sort)- 快速排序(Quick Sort)- 归并排序(Merge Sort)- 堆排序(Heap Sort)3. 实验步骤:(1)对每种排序算法进行编写和实现;(2)将测试数据分别输入到每种排序算法中,记录排序过程的时间;(3)比较不同排序算法的排序时间,分析其效率;(4)观察排序过程中数据的变化,分析其稳定性;(5)总结各种排序算法的适用场景。
四、实验结果与分析1. 冒泡排序(Bubble Sort)- 效率:时间复杂度为O(n^2),空间复杂度为O(1);- 稳定性:稳定排序算法;- 适用场景:数据量较小、基本有序的数据。
2. 选择排序(Selection Sort)- 效率:时间复杂度为O(n^2),空间复杂度为O(1);- 稳定性:不稳定排序算法;- 适用场景:数据量较小、基本有序的数据。
3. 插入排序(Insertion Sort)- 效率:时间复杂度为O(n^2),空间复杂度为O(1);- 稳定性:稳定排序算法;- 适用场景:数据量较小、基本有序的数据。
4. 快速排序(Quick Sort)- 效率:平均时间复杂度为O(nlogn),最坏情况时间复杂度为O(n^2),空间复杂度为O(logn);- 稳定性:不稳定排序算法;- 适用场景:数据量较大、基本有序或无序的数据。
5. 归并排序(Merge Sort)- 效率:时间复杂度为O(nlogn),空间复杂度为O(n);- 稳定性:稳定排序算法;- 适用场景:数据量较大、基本有序或无序的数据。
算法工程师面试真题单选题100道及答案解析

算法工程师面试真题单选题100道及答案解析1. 以下哪种数据结构适合用于实现快速查找最大值和最小值?A. 栈B. 队列C. 堆D. 链表答案:C解析:堆可以快速地获取最大值和最小值。
2. 快速排序在最坏情况下的时间复杂度是?A. O(nlogn)B. O(n^2)C. O(n)D. O(logn)答案:B解析:快速排序在最坏情况下,每次划分都极不均匀,时间复杂度为O(n^2)。
3. 以下哪种算法常用于在未排序的数组中查找特定元素?A. 冒泡排序B. 二分查找C. 顺序查找D. 插入排序答案:C解析:顺序查找适用于未排序的数组查找特定元素。
4. 一个有向图的邻接表存储结构中,顶点的邻接点是按照什么顺序存储的?A. 随机顺序B. 顶点编号的大小顺序C. 插入的先后顺序D. 无法确定答案:C解析:邻接表中顶点的邻接点是按照插入的先后顺序存储的。
5. 深度优先搜索遍历图的时间复杂度是?A. O(n)B. O(n + e)C. O(n^2)D. O(e)答案:B解析:深度优先搜索遍历图的时间复杂度为O(n + e),其中n 是顶点数,e 是边数。
6. 以下哪种排序算法是稳定的排序算法?A. 快速排序B. 希尔排序C. 冒泡排序D. 选择排序答案:C解析:冒泡排序是稳定的排序算法。
7. 一个具有n 个顶点的无向完全图,其边的数量为?A. n(n - 1) / 2B. n(n - 1)C. n^2D. 2n答案:A解析:无向完全图的边数为n(n - 1) / 2 。
8. 动态规划算法的基本思想是?A. 分治法B. 贪心算法C. 把问题分解成多个子问题并保存子问题的解D. 回溯法答案:C解析:动态规划的基本思想是把问题分解成多个子问题并保存子问题的解,避免重复计算。
9. 以下关于哈希表的说法,错误的是?A. 哈希表的查找时间复杂度为O(1)B. 哈希冲突可以通过开放定址法解决C. 哈希表的空间复杂度是固定的D. 哈希函数的设计会影响哈希表的性能答案:C解析:哈希表的空间复杂度不是固定的,取决于元素数量和负载因子等。
7排序

数据结构
例: 初始
49 38 65 97 76 13 27
48
55
4
取d1=5 一趟分组:49 38 65 97 76 13 27
48
55
4
一趟排序:13 27 48 55 4
49 38
65
97
76
取d2=3 13 27 48 55 4 二趟分组:
49 38
65
97
76
二趟排序:13
4
48 38 27
做出相应的调整,那样排序的总体效率会非常高。
• 堆排序(Heap Sort)就是对简单选择排序的一种改进。堆排序 算法是Floyd和Williams在1964年共同发明,同时发明了“堆” 这样的数据结构。
数据结构
堆的定义
一、定义
16 11 9
10 1 2
2
5 4
6
大顶堆
8
1
6 9
小顶堆
数据结构
11 4 12 16 5
• R[0]为监视哨(Sentinel),省略下标越界检查“j>0”:一 旦越界,j=0<1,循环条件R[0]<R[j]不成立,自动控制 while循环的结束。
例:有监视哨,第3趟
0 1 2 3 4 5 6 7 8 9 10 m
初始:
i=3: j=2: j=1: j=0:
49 38 13 76 27 49 38 49 13 76 27 49 13 38 49 13 76 27 49 13 38 49 49 76 27 49 13 38 38 49 76 27 49 13 13 38 49 76 27 49
8
堆的定义
一、定义 • 堆是具有下列性质的完全二叉树:任一结点关键字大于等于 其孩子结点的关键字,称为大顶堆;任一结点关键字小于等 于其孩子结点的关键字,称为小顶堆。 • 将R[1]到R[n]看成完全二叉树的顺序存储结构。根据二叉树 的性质5,如果双亲下标为i,其左右孩子的下标分别为2i和 2i+1. 当且仅当任一R[i]满足以下关系时,称之为堆: R[i] ≤ R[2i]且R[i] ≤ R[2i+1] (1 ≤ i ≤ n/2) 或者 R[i] ≥ R[2i]且R[i] ≥ R[2i+1] (1 ≤ i ≤ n/2) 且分别称之为小顶堆和大顶堆。
秩和比法及其应用

秩和比法及其应用
排序(Sorting)是将对象进行某种特定顺序方式排列的过程,常见的
排序方法有冒泡排序、插入排序、选择排序、归并排序、快速排序等。
秩和比法及其应用:
(1)秩和比法:
秩和比法(rank and comparison method)是一种比较排序,它主要采用
比较的方式进行排序,将待排序的数据拆分成一个个独立的元素,比
较每个元素大小来决定最终的排序结果。
当所有的元素都排序完毕,
最终的排序结果就可以得出。
(2)秩和比法的优点:
1、运算简单快速:秩和比法采用比较的方式进行排序,比较容易理解,算法复杂度较低,是一种时间复杂度较低的排序算法。
2、稳定性强:秩和比法属于稳定性排序,在处理顺序相同的数据中,
经过排序后,序列仍然保持原有的相对位置关系不变。
(3)秩和比法的应用:
1、比如常见的选择排序,是以比较的方式进行排序的,它的步骤是先
比较出当前序列元素中最小的那一个,然后将它与当前序列的首元素
位置进行交换,这就是秩和比法的应用;
2、秩和比法还可以用来排序大量数据,比如排序比赛中的参赛者,先比较出各参赛者的最佳成绩,再按照成绩将参赛者排序,就是秩和比法的应用。
基数与基数排序算法的分析

基数与基数排序算法的分析基数是数学中的概念,指的是进位计数法中的一个进制数。
在计算机科学中,基数常常用来描述不同进制的数系统。
基数排序算法则是一种用于排序的算法,它根据待排序数值的每个位上的数值进行比较和排序。
1. 基数的概念基数是进位计数法中的一个进制数,它决定了数系统中数码的个数和代表的数值范围。
在十进制中,我们使用的基数是10,因为数字的范围是0到9。
而在二进制中,我们使用的基数是2,因为数字的范围是0和1。
同样,八进制使用的是基数8,而十六进制使用的是基数16。
2. 基数排序算法的原理基数排序算法是一种分配式排序,它根据待排序数值的每个位上的数值进行比较和排序。
基数排序算法的基本原理是将待排序的数值按照位数逐个比较,从最低位到最高位进行排序。
在每一位上,将数值分配到对应的桶中,并按照顺序收集起来。
每一轮比较和排序完成之后,数值就变得有序了。
3. 基数排序算法的步骤基数排序算法的步骤如下:- 首先,确定待排序数值中的最高位数,并获取最高位数的基数。
将待排序数值按照最低位的数值进行一次排序。
- 接下来,将排序后的数值重新组合起来,形成新的待排序数值集合,并按照次低位的数值进行排序。
- 重复以上步骤,直到到达最高位进行排序。
最后,得到的数值集合就是有序的结果。
4. 基数排序算法的性能分析基数排序算法的时间复杂度为O(k*n),其中k为最高位数,n为待排序数值的个数。
相比于其他排序算法,基数排序算法具有稳定性和较高的效率。
但是,基数排序算法的空间复杂度较高,需要额外的存储空间来存放中间结果。
5. 基数排序的应用场景基数排序算法常常用于待排序数值比较大、位数较多的情况下。
它适用于各种数据类型,包括整数、浮点数、字符串等。
在实际应用中,基数排序算法被广泛用于大型数据集的排序、计算机图形学和加密算法等领域。
总结:基数与基数排序算法是计算机科学中的重要概念和算法。
基数确定了数系统中数码的个数和代表的数值范围,而基数排序则是根据待排序数值的每个位上的数值进行排序。
有关冒泡排序的总结

有关冒泡排序的总结
冒泡排序是一种简单但效率较低的排序算法,它重复地比较相邻的元素,并按照升序或降序交换位置,直到整个数组排序完成。
以下是对冒泡排序的总结:
1. 基本原理:冒泡排序的基本思想是通过相邻元素的比较和交换来实现排序。
每一轮排序会将未排序部分中的最大(或最小)元素冒泡到最后的位置。
2. 算法步骤:
- 从数组的第一个元素开始,比较相邻的两个元素。
- 如果顺序不正确,交换这两个元素的位置。
- 继续向后遍历,重复上述比较和交换的过程,直到最后一个元素。
- 重复以上步骤,直到所有元素都排好序。
3. 时间复杂度:冒泡排序的时间复杂度为O(n^2),其中n 是待排序数组的元素个数。
最好情况下,如果待排序数组已经有序,冒泡排序可以通过设置一个标志位来提前结束,此时时间复杂度为O(n)。
4. 空间复杂度:冒泡排序的空间复杂度为O(1),它只需要一个临时变量用于交换元素的位置。
5. 稳定性:冒泡排序是稳定的,即相等元素的相对位置在排序前后保持不变。
6. 优缺点:
- 优点:冒泡排序的实现简单,易于理解和实现。
- 缺点:冒泡排序的效率较低,尤其在处理大规模数据时。
它需要多次比较和交换操作,即使数组已经有序,也需要进行完整的遍历。
总而言之,冒泡排序是一种简单但效率较低的排序算法,适用于小规模的数据排序。
但在实际应用中,通常会选择更高效的排序算法,如快速排序、归并排序等。
数据结构第9章 排序
数据结构第9章排序数据结构第9章排序第9章排名本章主要内容:1、插入类排序算法2、交换类排序算法3、选择类排序算法4、归并类排序算法5、基数类排序算法本章重点难点1、希尔排序2、快速排序3、堆排序4.合并排序9.1基本概念1.关键字可以标识数据元素的数据项。
如果一个数据项可以唯一地标识一个数据元素,那么它被称为主关键字;否则,它被称为次要关键字。
2.排序是把一组无序地数据元素按照关键字值递增(或递减)地重新排列。
如果排序依据的是主关键字,排序的结果将是唯一的。
3.排序算法的稳定性如果要排序的记录序列中多个数据元素的关键字值相同,且排序后这些数据元素的相对顺序保持不变,则称排序算法稳定,否则称为不稳定。
4.内部排序与外部排序根据在排序过程中待排序的所有数据元素是否全部被放置在内存中,可将排序方法分为内部排序和外部排序两大类。
内部排序是指在排序的整个过程中,待排序的所有数据元素全部被放置在内存中;外部排序是指由于待排序的数据元素个数太多,不能同时放置在内存,而需要将一部分数据元素放在内存中,另一部分放在外围设备上。
整个排序过程需要在内存和外存之间进行多次数据交换才能得到排序结果。
本章仅讨论常用的内部排序方法。
5.排序的基本方法内部排序主要有5种方法:插入、交换、选择、归并和基数。
6.排序算法的效率评估排序算法的效率主要有两点:第一,在一定数据量的情况下,算法执行所消耗的平均时间。
对于排序操作,时间主要用于关键字之间的比较和数据元素的移动。
因此,我们可以认为一个有效的排序算法应该是尽可能少的比较和数据元素移动;第二个是执行算法所需的辅助存储空间。
辅助存储空间是指在一定数据量的情况下,除了要排序的数据元素所占用的存储空间外,执行算法所需的存储空间。
理想的空间效率是,算法执行期间所需的辅助空间与要排序的数据量无关。
7.待排序记录序列的存储结构待排序记录序列可以用顺序存储结构和和链式存储结构表示。
在本章的讨论中(除基数排序外),我们将待排序的记录序列用顺序存储结构表示,即用一维数组实现。
各种排序算法的时间复杂度和空间复杂度(阿里)
各种排序算法的时间复杂度和空间复杂度(阿⾥)⼆分查找法的时间复杂度:O(logn) redis,kafka,B+树的底层都采⽤了⼆分查找法参考:⼆分查找法 redis的索引底层的跳表原理实现参考:⼆分查找法参考:⼆分查找法:1.⼆分查找⼆分查找也称为折半查找,它是⼀种效率较⾼的查找⽅法。
⼆分查找的使⽤前提是线性表已经按照⼤⼩排好了序。
这种⽅法充分利⽤了元素间的次序关系,采⽤分治策略。
基本原理是:⾸先在有序的线性表中找到中值,将要查找的⽬标与中值进⾏⽐较,如果⽬标⼩于中值,则在前半部分找,如果⽬标⼩于中值,则在后半部分找;假设在前半部分找,则再与前半部分的中值相⽐较,如果⼩于中值,则在中值的前半部分找,如果⼤于中值,则在后半部分找。
以此类推,直到找到⽬标为⽌。
假设我们要在 2,6,11,13,16,17,22,30中查找22,上图所⽰,则查找步骤为:⾸先找到中值:中值为13(下标:int middle = (0+7)/2),将22与13进⾏⽐较,发现22⽐13⼤,则在13的后半部分找;在后半部分 16,17,22,30中查找22,⾸先找到中值,中值为17(下标:int middle=(0+3)/2),将22与17进⾏⽐较,发现22⽐17⼤,则继续在17的后半部分查找;在17的后半部分 22,30查找22,⾸先找到中值,中值为22(下标:int middle=(0+1)/2),将22与22进⾏⽐较,查找到结果。
⼆分查找⼤⼤降低了⽐较次数,⼆分查找的时间复杂度为:O(logn),即。
⽰例代码:public class BinarySearch {public static void main(String[] args) {int arr[] = {2, 6, 11, 13, 16, 17, 22, 30};System.out.println("⾮递归结果,22的位置为:" + binarySearch(arr, 22));System.out.println("递归结果,22的位置为:" + binarySearch(arr, 22, 0, 7));}//⾮递归static int binarySearch(int[] arr, int res) {int low = 0;int high = arr.length-1;while(low <= high) {int middle = (low + high)/2;if(res == arr[middle]) {return middle;}else if(res <arr[middle]) {high = middle - 1;}else {low = middle + 1;}}return -1;}//递归static int binarySearch(int[] arr,int res,int low,int high){if(res < arr[low] || res > arr[high] || low > high){return -1;}int middle = (low+high)/2;if(res < arr[middle]){return binarySearch(arr, res, low, middle-1);}else if(res > arr[middle]){return binarySearch(arr, res, middle+1, high);}else {return middle;}}}其中冒泡排序加个标志,所以最好情况下是o(n)直接选择排序:排序过程:1 、⾸先在所有数据中经过 n-1次⽐较选出最⼩的数,把它与第 1个数据交换,2、然后在其余的数据内选出排序码最⼩的数,与第 2个数据交换...... 依次类推,直到所有数据排完为⽌。
排序教学应注意的问题
在教排序算法时,应注意以下几个问题:
1 选择合适的排序算法:根据样本的大小、数据的类型、算法的时
间复杂度和空间复杂度等因素,选择合适的排序算法。
2 讲解排序算法的原理:详细讲解排序算法的基本原理,使学生理
解其工作原理。
3 给出足够多的例子:给出足够多的排序例子,帮助学生理解排序
算法的具体应用。
4 强调算法的时间复杂度:强调排序算法的时间复杂度,帮助学生
选择合适的排序算法。
5 关注代码实现细节:关注排序算法的代码实现细节,避免学生在
代码实现时出错。
6 强调代码的可读性:教会学生写出可读性较高的代码,方便他人
理解和维护。
7讨论常见的优化方法:讨论排序算法的常见优化方法,如记忆化、分治法等,帮助学生进一步提高算法的效率。
8 讲解常见的排序算法:教授足够多的常见排序算法,如冒泡排序、
插入排序、选择排序、快速排序、归并排序等,让学生有足够的选择余地。
9 讲解排序算法的可扩展性:讲解排序算法在处理大规模数据时的
可扩展性,帮助学生了解如何优化算法的效率。
10讲解排序算法的稳定性:讲解排序算法的稳定性,即相同大小的数据在排序后相对位置是否发生变化,帮助学生了解如何选择合适的排序算法。
经典十大排序算法
经典⼗⼤排序算法前⾔排序种类繁多,⼤致可以分为两⼤类:⽐较类排序:属于⾮线性时间排序,时间复杂度不能突破下界O(nlogn);⾮⽐较类排序:能达到线性时间O(n),不是通过⽐较来排序,有基数排序、计数排序、桶排序。
了解⼀个概念:排序的稳定性稳定是指相同⼤⼩的元素多次排序能保证其先后顺序保持不变。
假设有⼀些学⽣的信息,我们先根据他们的姓名进⾏排序,然后我们还想根据班级再进⾏排序,如果这时使⽤的时不稳定的排序算法,那么第⼀次的排序结果可能会被打乱,这样的场景需要使⽤稳定的算法。
堆排序、快速排序、希尔排序、选择排序是不稳定的排序算法,⽽冒泡排序、插⼊排序、归并排序、基数排序是稳定的排序算法。
1、冒泡排序⼤多数⼈学编程接触的第⼀种排序,名称很形象。
每次遍历排出⼀个最⼤的元素,将⼀个最⼤的⽓泡冒出⽔⾯。
时间复杂度:平均:O(n2);最好:O(n);最坏:O(n2)空间复杂度:O(1)public static void bubbleSort(int[] arr) {/*** 总共⾛len-1趟即可,每趟排出⼀个最⼤值放在最后*/for (int i = 0; i < arr.length - 1; i++) {for (int j = 0; j < arr.length - i - 1; j++) {if (arr[j] > arr[j + 1]) {int tp = arr[j];arr[j] = arr[j + 1];arr[j + 1] = tp;}}}}2、选择排序最直观易理解的排序算法,每次排出⼀个最⼩的元素。
也是最稳定的算法,时间复杂度稳定为O(n^2)。
需要⼀个变量记录每次遍历最⼩元素的位置。
时间复杂度:O(n2)空间复杂度:O(1)public static void selectSort(int[] arr){int n = arr.length;for (int i = 0; i < n; i++) {int maxIdx = 0;for(int j = 1; j < n - i; j++){if(arr[maxIdx] < arr[j]){maxIdx = j;}}int tp = arr[maxIdx];arr[maxIdx] = arr[n - 1 - i];arr[n - 1 - i] = tp;}}3、插⼊排序⼀种直观的排序算法,从第⼆个元素开始,每次往前⾯遍历找到⾃⼰该在的位置。
- 1、下载文档前请自行甄别文档内容的完整性,平台不提供额外的编辑、内容补充、找答案等附加服务。
- 2、"仅部分预览"的文档,不可在线预览部分如存在完整性等问题,可反馈申请退款(可完整预览的文档不适用该条件!)。
- 3、如文档侵犯您的权益,请联系客服反馈,我们会尽快为您处理(人工客服工作时间:9:00-18:30)。
各种排序算法稳定性的探讨
首先,排序算法的稳定性大家应该都知道,通俗地讲就是能保证排序前2个相等的数其在
序列的前后位置顺序和排序后它们两个的前后位置顺序相同。在简单形式化一下,如果Ai =
Aj, Ai原来在位置前,排序后Ai还是要在Aj位置前。为了简便下面讨论的都是不降序排列
的情形,对于不升序排列的情形讨论方法和结果完全相同。
其次,说一下稳定性的好处。排序算法如果是稳定的,那么从一个键上排序,然后再从另
一个键上排序,第一个键排序的结果可以为第二个键排序所用。基数排序就是这样,先按低
位排序,逐次按高位排序,低位相同的元素其顺序再高位也相同时是不会改变的。另外,如
果排序算法稳定,对基于比较的排序算法而言,元素交换的次数可能会少一些(个人感觉,
没有证实)。
回到主题,现在分析一下常见的排序算法的稳定性,每个都给出简单的理由。
(1)冒泡排序
冒泡排序是通过相邻比较、实时交换、缩小范围实现排序的。第1次操作n个元素,通
过相邻比较将0~n-1中的最大元素交换到位置n-1上,第2次操作n-1个元素,通过相邻比
较将0~n-2中的最大元素交换到位置n-2上……第n-1次操作2个元素,通过相邻比较将0~1
上的最大元素交换到位置1上完成排序。在相邻比较时如果两个元素相等,一般不执行交换
操作,因此冒泡排序是一种稳定排序算法。
(2)选择排序
选择排序是通过不断缩小排序序列长度来实现的。第1次操作n个元素,选择0~n-1中
的最小者交换到位置0上,第2次操作n-1个元素,选择1~n-1中的最小者交换到位置1上……
第n-1次操作2个元素,选择n-2~n-1上的最小者交换到位置n-2上完成排序。在每次选择
最小元素进行交换时,可能破坏稳定性。这种情况可以描述为:约定要发生交换的位置称为
当前位置,被交换的位置称为被交换位置,被交换位置上的元素为选中的最小元素。如果当
前位置之后和被交换位置之前存在与当前位置相等的元素,执行交换后就破坏了稳定性。如
序列5 8 5 2 9,我们知道第一遍选择第1个元素5会和2交换,那么原序列中2个5的相对
前后顺序就被破坏了,所以选择排序不是一个稳定的排序算法。
(3)插入排序
插入排序是通过不断扩大排序序列的长度来实现的。第1次操作1个元素,直接放到位置
0上即可;第2次操作2个元素,在0~1上为当前元素找到合适位置并插入;第3次操作3
个元素,用在0~2上为当前元素找到合适位置并插入它……第n次操作n个元素,在0~n-1
上为当前元素找到合适位置并插入完成排序。讨论元素的插入过程,假设当前是第n次操作,
要在0~n-1上为当前元素寻找合适位置,设置一个工作指针初始化为n-1,向前移动工作指
针直到遇到一个不大于当前元素的元素,就在这个元素的后面插入当前元素,仔细体会这个
插入过程,不难理解插入排序是稳定的。
(4)快速排序
快速排序有两个方向,左边的i下标当a[i] <= a[center]时一直往右走,其中center是中枢
元素的数组下标,一般取为当前排序段的第一个元素。而右边的j下标当a[j] > a[center]时一
直往左走。如果i和j都走不动了,这时必有结论a[i] > a[center] >= a[j],我们的目的是将a
分成不大于a[center]和大于a[center]的两个部分,其中前者位于左半部分后者位于右半部分。
所以如果i>j(i不能等于j,为什么?)表明已经分好,否则需要交换两者。当左右分好时,j
指向了左侧的最后一个元素,这时需要将a[center]与a[j],交换,这个时侯可能会破坏稳定性。
这种情形可以这样描述:center位置之后j位置前存在与j相等的元素,指向center与j的交
换后,稳定性破坏。比如序列为 5 3 3 4 3 8 9 10 11,现在中枢元素5和3(第5个元素,下
标从1开始计)交换就会把元素3的稳定性打乱,所以快速排序是一个不稳定的排序算法。
(5)归并排序
归并排序是把序列递归地分成短序列,递归出口是短序列只有1个元素(认为直接有序)或
者2个序列(1次比较和交换),然后把各个有序的段序列合并成一个有序的长序列,不断合并
直到原序列全部排好序。可以发现,在1个或2个元素时,1个元素不会交换,2个元素如
果大小相等也没有人故意交换,这不会破坏稳定性。那么,在短的有序序列合并的过程中,
稳定是是否受到破坏?没有,合并过程中我们可以保证如果两个当前元素相等时,我们把处
在前面的序列的元素保存在结果序列的前面,这样就保证了稳定性。所以,归并排序也是稳
定的排序算法。
(6)基数排序
基数排序是按照低位先排序,然后收集;再按照高位排序,然后再收集;依次类推,直到
最高位。有时候有些属性是有优先级顺序的,先按低优先级排序,再按高优先级排序,最后
的次序就是高优先级高的在前,高优先级相同的低优先级高的在前。基数排序基于分别排序,
分别收集,所以其是稳定的排序算法。
(7)希尔排序(shell)
希尔排序是按照不同步长对元素进行插入排序,当刚开始元素很无序的时候,步长最大,
所以插入排序的元素个数很少,速度很快;当元素基本有序了,步长很小,插入排序对于有
序的序列效率很高。所以,希尔排序的时间复杂度会比o(n^2)好一些。由于多次插入排序,
我们知道一次插入排序是稳定的,不会改变相同元素的相对顺序,但在不同的插入排序过程
中,相同的元素可能在各自的插入排序中移动,最后其稳定性就会被打乱,所以shell排序
是不稳定的。
(8)堆排序
我们知道基于0序的堆结构,节点i的孩子为2*i+1和2*i+2节点,大顶堆要求父节点大
于等于其2个子节点,小顶堆要求父节点小于等于其2个子节点。在一个长为n的序列,堆
排序的过程是从第n/2开始和其子节点共3个值选择最大(大顶堆)或者最小(小顶堆),这3个
元素之间的选择当然不会破坏稳定性。但当为n/2-1, n/2-2, ...1这些个父节点选择元素时,就
会破坏稳定性。有可能第n/2个父节点交换把后面一个元素交换过去了,而第n/2-1个父节
点把后面一个相同的元素没有交换,那么这2个相同的元素之间的稳定性就被破坏了。所以,
堆排序不是稳定的排序算法。
通过上面的论述不难发现规律:存在不相邻交换的排序算法一般是不稳定的,相邻交换的
排序算法一般是稳定的;对于相邻交换的稳定排序算法,通过控制交换条件可以转换成不稳
定排序算法;冒泡、插入、归并和基数排序是稳定的;选择、快速、希尔和堆排序是不稳定
的。
排序算法的性能可以由排序过程中比较次数来衡量。待排序的初始化顺序有可能影响某个具
体排序算法的性能,下面我对常用排序算法的性能与待排数组的循序的关系作一个总结:
1)冒泡:无关; 2)选择:无关;
3)插入:有关,排序程度越大,比较越少;
4)shell:有关,它的基本思想基于插入排序;
5)融合:有关,排序程度愈大,融合过程的比较次数越少;
6)堆排序:有关,排序程度越大,建立堆下沉操作越少;
7)快排序:有关,如果选择最后值作为阀值,那么排序程度越好,就越可能退化成O(n^2);
无关,随机选择阀值,那么与排序程度无关。