排序算法总结

排序算法

表格速览

排序算法平均复杂度最坏复杂度最佳复杂度空间复杂度稳定性复杂性
插入排序 O ( n 2 ) O(n^2) O(n2) O ( n 2 ) O(n^2) O(n2) O ( n ) O(n) O(n) O ( 1 ) O(1) O(1)稳定简单
冒泡排序 O ( n 2 ) O(n^2) O(n2) O ( n 2 ) O(n^2) O(n2) O ( n ) O(n) O(n) O ( 1 ) O(1) O(1)稳定简单
快速排序 O ( n l o g 2 n ) O(nlog_{2}n) O(nlog2n) O ( n 2 ) O(n^2) O(n2) O ( n l o g 2 n ) O(nlog_{2}n) O(nlog2n) O ( l o g 2 n ) O(log_{2}n) O(log2n)不稳定较复杂
选择排序 O ( n 2 ) O(n^2) O(n2) O ( n 2 ) O(n^2) O(n2) O ( n 2 ) O(n^2) O(n2) O ( 1 ) O(1) O(1)不稳定简单
堆排序 O ( n l o g 2 n ) O(nlog_{2}n) O(nlog2n) O ( n l o g 2 n ) O(nlog_{2}n) O(nlog2n) O ( n l o g 2 n ) O(nlog_{2}n) O(nlog2n) O ( 1 ) O(1) O(1)不稳定较复杂
归并排序 O ( n l o g 2 n ) O(nlog_{2}n) O(nlog2n) O ( n l o g 2 n ) O(nlog_{2}n) O(nlog2n) O ( n l o g 2 n ) O(nlog_{2}n) O(nlog2n) O ( n ) O(n) O(n)稳定较复杂

具体实现

插入排序

第一步:把待排序的数组分成已排序和未排序两部分,初始的时候把第一个元素认为是已排好序的。

第二步:从第二个元素开始,在已排好序的子数组中寻找到该元素合适的位置并插入该位置。

重复上述过程直到最后一个元素被插入有序子数组中

代码优化

插入排序由于O( n2 )的复杂度,在数组较大的时候不适用。但是,在数据比较少的时候,是一个不错的选择,一般做为快速排序的扩充。例如,在STL的sort算法和stdlib的qsort算法中,都将插入排序作为快速排序的补充,用于少量元素的排序。又如,在JDK 7 java.util.Arrays所用的sort方法的实现中,当待排数组长度小于47时,会使用插入排序。

冒泡排序

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

代码优化

在数据完全有序的时候展现出最优时间复杂度,为O(n)。其他情况下,几乎总是O( n2 )。因此,算法在数据基本有序的情况下,性能最好。要使算法在最佳情况下有O(n)复杂度,需要做一些改进,增加一个swap的标志,当前一轮没有进行交换时,说明数组已经有序,没有必要再进行下一轮的循环了,直接退出。

public static void bubbleSort(int[] arr) {
    int temp = 0;
    boolean swap;
    for (int i = arr.length - 1; i > 0; i--) { // 每次需要排序的长度
        swap=false;
        for (int j = 0; j < i; j++) { // 从第一个元素到第i个元素
            if (arr[j] > arr[j + 1]) {
                temp = arr[j];
                arr[j] = arr[j + 1];
                arr[j + 1] = temp;
                swap=true;
            }
        }//loop j
        if (swap==false){
            break;
        }
    }//loop i
}// method bubbleSort

快速排序

第一步:从数列中挑出一个元素,称为"基准"(pivot)

第二步:重新排序数列,所有比基准值小的元素摆放在基准前面,所有比基准值大的元素摆在基准后面(相同的数可以到任何一边)。在这个分区结束之后,该基准就处于数列的中间位置。这个称为分区(partition)操作。

第三步:递归地(recursively)把小于基准值元素的子数列和大于基准值元素的子数列排序。

稳定性与适用场景

快速排序并不是稳定的。这是因为我们无法保证相等的数据按顺序被扫描到和按顺序存放。快速排序在大多数情况下都是适用的,尤其在数据量大的时候性能优越性更加明显。但是在必要的时候,需要考虑下优化以提高其在最坏情况下的性能。

选择排序

第一步:在未排序序列中找到最小(大)元素,存放到排序序列的起始位置。

第二步:从剩余未排序元素中继续寻找最小(大)元素,然后放到已排序序列的末尾。

重复前两步,直到所有元素均排序完毕。

代码优化

用数组实现的选择排序是不稳定的,用链表实现的选择排序是稳定的。不过,一般提到排序算法时,大家往往会默认是数组实现,所以选择排序是不稳定的。但是由于固有的O(n2)复杂度,选择排序在海量数据面前显得力不从心。因此,它适用于简单数据排序。

堆排序

堆排序就是把最大堆堆顶的最大数取出,将剩余的堆继续调整为最大堆,再次将堆顶的最大数取出,这个过程持续到剩余数只有一个时结束。

稳定性与适用场景

堆排序存在大量的筛选和移动过程,属于不稳定的排序算法。堆排序在建立堆和调整堆的过程中会产生比较大的开销,在元素少的时候并不适用。但是,在元素比较多的情况下,还是不错的一个选择。尤其是在解决诸如“前n大的数”一类问题时,几乎是首选算法。

归并排序

递归法

第一步:申请空间,使其大小为两个已经排序序列之和,该空间用来存放合并后的序列

第二步:设定两个指针,最初位置分别为两个已经排序序列的起始位置

第三步:比较两个指针所指向的元素,选择相对小的元素放入到合并空间,并移动指针到下一位置
重复步骤3直到某一指针到达序列尾

第四步:将另一序列剩下的所有元素直接复制到合并序列尾

迭代法

第一步:将序列每相邻两个数字进行归并操作,形成n/2个序列,排序后每个序列包含一个或两个元素

第二步:若此时序列数不是1个则将上述序列再次归并,形成n/4个序列,每个序列包含四个或者三个元素

重复步骤2,直到所有元素排序完毕,即序列数为1

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值