各大排序算法对比

 

 

 

 

算法简述逻辑做法最坏平均最好空间复杂度稳定性复杂性重点测试(100000)实际效率(以随机下冒泡为基准)优点缺点评价
冒泡 冒泡排序(Bubble Sort),又被称为气泡排序或泡沫排序。 它是一种较简单的排序算法。它会遍历若干次要排序的数列,每次遍历时,它都会从前往后依次的比较相邻两个数的大小;如果前者比后者大,则交换它们的位置。这样,一次遍历之后,最大的元素就在数列的末尾! 采用相同的方法再次遍历时,第二大的元素就被排列在最大元素之前。重复此操作,直到整个数列都有序为止! 两层循环。 1.第一层从后往前,第二层从前往后,直到第一层的index,这样每轮选出最大的数,同时只比较到上轮最大数之前的数。 2.内层循环则进行比较,判断是否需要交换。 O(n²) 逆序 O(n²) O(n) 顺序,相同 O(1) 稳定 简单 1.比较不需要每轮都到底。 2.每轮比较发现没有需要交换的,说明已经有序,直接跳出。 升序:3 降序:15670 重复:0 随机:18347 升序:100% 降序:100% 重复:100% 随机:100%    
插入 直接插入排序(Straight Insertion Sort)的基本思想是:把n个待排序的元素看成为一个有序表和一个无序表。开始时有序表中只包含1个元素,无序表中包含有n-1个元素,排序过程中每次从无序表中取出第一个元素,将它插入到有序表中的适当位置,使之成为新的有序表,重复n-1次可完成排序过程。 两层循环,下标1的数已经有序,所以第一层从1开始,用临时变量保存待插入的数组值,第二层循环从后往前依次比较,判断是否交换,直到待比较数组值小于临时变量为止break,break很重要 O(n²) 逆序 O(n²) O(n) 顺序,相同 O(1) 稳定 简单 1.第二层比较必须从后往前,减少交换或者移动的次数。 2.第二层比较结束一定要break。 升序:3 降序:7956 重复:0 随机:1983 升序:100% 降序:50.8% 重复:100% 随机:10.8%    
希尔 希尔排序(Shell Sort)是插入排序的一种,它是针对直接插入排序算法的改进。该方法又称缩小增量排序,因DL.Shell于1959年提出而得名。 希尔排序实质上是一种分组插入方法。它的基本思想是:对于n个待排序的数列,取一个小于n的整数gap(gap被称为步长)将待排序元素分成若干个组子序列,所有距离为gap的倍数的记录放在同一个组中;然后,对各组内的元素进行直接插入排序。 这一趟排序完成之后,每一个组的元素都是有序的。然后减小gap的值,并重复执行上述的分组和排序。重复这样的操作,当gap=1时,整个数列就是有序的。 好理解的写法,就是纯按简述逻辑写,需要4层循环。 其实3层就可以了。 第一层控制gap,每次缩小2分之一(缩小逻辑可以换层更科学的),第二层从gap开始,每次加1。这样就可以,不需要再分出一个循环来做每个组。加1自然覆盖到每个组了。第三层从上一层的i开始,每次比较i-gap,每次i=i-gap。其它逻辑和直接插入一致。 像交于直接插入排序,减少了二层循环的次数 O(n2) 1.3~2方 O(nlog2n) O(n1.3) O(1) 不稳定 较复杂 其实代码量比直接插入没多几行 第二层gap开始,每次加1,就能覆盖每个组了,不需要为每个组单独开一个循环。 升序:13 降序:10 重复:1 随机:15 4层循环 升序:10 降序:7 重复:4 随机:14 升序:1000%以上 降序:0.06% 重复:1000%以上 随机:0.08% 效率也很好,就是不稳定,如果空间有要求,且稳定性也不考虑,可以使用,如果空间没有要求,但对稳定性有考虑,可以使用归并排序。 效率平均且高效
选择 选择排序(Selection sort)是一种简单直观的排序算法。 它的基本思想是:首先在未排序的数列中找到最小(or最大)元素,然后将其存放到数列的起始位置;接着,再从剩余未排序的元素中继续寻找最小(or最大)元素,然后放到已排序序列的末尾。以此类推,直到所有元素均排序完毕。 两层循环,第一层从小到大,控制二层循环每次找出最小的数,两个临时变量,一个保存带比较的“最小数组值”,一个记录“最小数组值”的下标,二层循环结束,交换最小值下标和第一层循环的i。 O(n²) O(n²) O(n²) O(1) 不稳定 简单   升序:1178 降序:4463 重复:1567 随机:1531 升序:1000%以上 降序:28.5% 重复:1000%以上 随机:8.3% 1.最多只有n个交换,交换次数小。 2.时间效率比较平均,且相对算高。  
归并 将两个的有序数列合并成一个有序数列,我们称之为"归并"。 归并排序(Merge Sort)就是利用归并思想对数列进行排序。根据具体的实现,归并排序包括"从上往下"和"从下往上"2种方式。 1. 从下往上的归并排序:将待排序的数列分成若干个长度为1的子数列,然后将这些数列两两合并;得到若干个长度为2的有序数列,再将这些数列两两合并;得到若干个长度为4的有序数列,再将它们两两合并;直接合并成一个数列为止。这样就得到了我们想要的排序结果。(参考下面的图片) 2. 从上往下的归并排序:它与"从下往上"在排序上是反方向的。它基本包括3步: ① 分解 -- 将当前区间一分为二,即求分裂点 mid = (low + high)/2; ② 求解 -- 递归地对两个子区间a[low...mid] 和 a[mid+1...high]进行归并排序。递归的终结条件是子区间长度为1。 ③ 合并 -- 将已排序的两个子区间a[low...mid]和 a[mid+1...high]归并为一个有序的区间a[low...high]。 分治加迭代。 一次迭代内分裂成两个子迭代。分裂点为2分之1。 两个子迭代完成后,都需要进行合并。 合并时需要零时数组来暂存。 合并逻辑“ head2 > e || (nums[head1] <= nums[head2] && head1 <= index)”。 即:在后半段指针结束或者前半段头指针数据小于或者等于后半段头指针数据,同时前半段头指针还未结束,则将前半段头指针数据加入临时数组中,否则将后半段头指针数据加入临时数组中。 O(nlog2n) O(nlog2n) O(nlog2n) O(n) 稳定 较复杂 可以利用1数存两值的技巧,将O(n)的空间复杂度优化到O(1)。 一数存两数算法 midVal = max(a,b) + 1 在这里延伸为 max(num[0],num[1],num[2],num[3],num[4]......)+1 temp = a + b * midVal。 a = temp % midVal b = temp / midVal 升序:10 降序:13 重复:5 随机:11 不使用临时数组暂存下: 升序:51 降序:39 重复:38 随机:36 升序:1000%以上 降序:0.08% 重复:1000%以上 随机:0.06% 不使用临时数组暂存下: 升序:1000%以上 降序:0.24% 重复:1000%以上 随机:0.20% 比选择排序更加平均, 比希尔排序稳定,效率还差不多。 唯一缺点,需要一倍的临时数组。 优化掉额外临时数组后,效率也很好。相交于临时数组慢了3倍 效率平均且高效
桶排序(Bucket Sort)的原理很简单,它是将数组分到有限数量的桶子里。 假设待排序的数组a中共有N个整数,并且已知数组a中数据的范围[0, MAX)。在桶排序时,创建容量为MAX的桶数组r,并将桶数组元素都初始化为0;将容量为MAX的桶数组中的每一个单元都看作一个"桶"。 在排序时,逐个遍历数组a,将数组a的值,作为"桶数组r"的下标。当a中数据被读取时,就将桶的值加1。例如,读取到数组a[3]=5,则将r[5]的值+1。 需要一个数组最大值长度临时数组,用于存储数组每个值在临时数组[数组值]的标记1。两个并列循环,第一个先做一遍原数组的循环,给临时数组[原数组值]++,第二个循环临时数组,根据值大小,添加几个下标到原数组中,以完成排序 O(n+k) O(n+k) O(n+k) O(n+m) 稳定 简单 原数组值作为临时数组的下标,临时数组某个下标值大于1,说明该下标值在原数组中出现了多次 升序:9 降序:3 重复:2 随机:5 升序:1000%以上 降序:0.02% 重复:1000%以上 随机:0.03% 如果原数组中存在大值,该排序极耗费空间,否者该排序效率最好,而且稳定。 如果原数组中值都很小,该算法效率最高。
基数 基数排序(Radix Sort)是桶排序的扩展,它的基本思想是:将整数按位数切割成不同的数字,然后按每个位数分别比较。 具体做法是:将所有待比较数值统一为同样的数位长度,数位较短的数前面补零。然后,从最低位开始,依次进行一次排序。这样从最低位排序一直到最高位排序完成以后, 数列就变成一个有序序列。 外层方法控制radix变化(每次乘10), 两个临时数组,第一个用于分成10个桶,另一个用于存储排好序后的原数组。 第一个循环构建10个桶里的数据,在用一个循环从后往前buckets[i] += buckets[i - 1];构建出每个分段最后一个数的下标。再一个循环冲后往前遍历原数组,更具桶里存的下标,放入临时数组,桶存的下标-1,最后将排好序的临时数组复制给原数组。 O(d*(n+k)) O(d*(n+k)) O(d*(n+k)) O(n+n+k) 稳定 较复杂 最后桶里存的是每个分段最后一个数的下标。 升序:24 降序:13 重复:9 随机:9 升序:1000%以上 降序:0.08% 重复:1000%以上 随机:0.05% 和桶排序相比,桶数量极小了,代价是需要多做基数增长的D次,如果原数组不均匀,理论上白白浪费了很多次n+k 还是归并排序牛逼
快排 快速排序(Quick Sort)使用分治法策略。 它的基本思想是:选择一个基准数,通过一趟排序将要排序的数据分割成独立的两部分;其中一部分的所有数据都比另外一部分的所有数据都要小。然后,再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。 快速排序流程: (1) 从数列中挑出一个基准值。 (2) 将所有比基准值小的摆放在基准前面,所有比基准值大的摆在基准的后面(相同的数可以到任一边);在这个分区退出之后,该基准就处于数列的中间位置。 (3) 递归地把"基准值前面的子数列"和"基准值后面的子数列"进行排序。 分治加迭代。 一次迭代内分裂成两个子迭代。分裂点为基数在数组中的位置。 每个迭代内,用两个并列的循环,分别重两端找各自的大于基数的值和小于基数的值,找到后等待交换,外面套一层循环控制什么时候结束while(i<j),比较时如果相等且i<j则i++,进入下一次比较,否则则交换 O(n²) 重复,就是基数找不好 O(nlog2n) O(nlog2n) O(1) 不稳定 较复杂 while (i < j && base > nums[i]) { //重点一:就在这里,因为base和nums没有 “=” 所以方向是交替,不会出现两边都同时++和--。 也所以根本就不需要方向。也得出最后的num[i] = base if (nums[i] == nums[j] && i<j) { //重点二:必须有这一步,不然存在重复的数组 会有死循环。 还有点 i 必须小于 j 否则,nums[i] == nums[j] 无意义 升序:2571 降序:2004 重复:18554 随机:18 升序:1000%以上 降序:12.8% 重复:1000%以上 随机:0.10% 都是渣渣,也就随机性能稍好点,重复下就是渣渣 渣渣,最怕重复

 

参考代码:https://gitee.com/wzgdxg/ksf

posted @ 2021-04-12 09:50  aoeiuv  阅读(362)  评论(0编辑  收藏  举报