美文网首页
排序算法总结

排序算法总结

作者: Miss_麦兜 | 来源:发表于2017-11-18 09:57 被阅读0次

    基本概念

    1. 时间复杂度

    • 定义:一个算法流程中,常数操作数量的指标,这个指标叫做O,big O。具体为,如果常数操作数量的表达式中,只要高阶项,不要低阶项,也不要高阶项系数之后,剩下的部分记为f(N),那么该算法的时间复杂度为O(f(N))
    • 常数操作与数据量没有关系,时间复杂度和数据量有关,只要高阶项,且不要系数。

    2. 空间复杂度

    • 定义:给的数组不占额外空间,额外空间是为了完成排序还需要多少辅助的空间。
    • 有限的几个变量是O(1),长度为N的数组为O(N)。

    3. 稳定性

    • 定义:一个数组在排完序后,相等的值的相对次序不发生变化。
    • 稳定性的作用:对引用类型的数据进行排序

    4. 测试用例

    随机生成数组,用自带的sort函数进行结果验证。引用类型数据还要写比对函数。

    5. 递归和迭代

    • 所有递归都可以改为迭代。递归是系统自己压栈,迭代就是自己压栈。
    • 递归是自己调用自己,迭代是用while循环。
    • 生产环境里不要用递归。

    基于比较的的排序

    时间复杂度不可能低于O(N*logN)

    一、冒泡排序

    • 时间复杂度:O(N^2)
    • 额外空间复杂度:O(1)
    • 是否可实现稳定性:是

    1. 原理

    从第一个数开始,比较相邻的两个数,大的数往下沉,每次排好未排序序列中的最大数。

    2. 交换两个数据的方法

    (1)用temp变量保存。

    var temp=a;
    a=b;
    b=temp;
    

    (2)异或运算(XOR)。
    异或满足交换律和结合律,就是无进位相加,1+0=1,0+1=1,1+1=0。
    缺点:float类型且引用地址相同不能用该方法。

    a=a^b;
    b=a^b;
    a=a^b;
    

    3. 时间复杂度

    N+N-1+....+1=N(N+1)/2;只要高阶项,不要低阶项,也不要高阶项系数,因此为O(N²)。

    4. 空间复杂度

    冒泡排序只需要几个有限的变量空间,所以是O(1)。

    5. 稳定性

    相等的不交换,大于时交换,就可以保证稳定性。

    二、插入排序

    • 时间复杂度O(N^2)
    • 额外空间复杂度O(1)
    • 是否可实现稳定性:是

    1. 原理(类似扑克牌)

    插入排序就是,在有序区中,从后往前,遇到大于自身的数就进行交互换,看什么时候停下来。直到最后一个数加入有序区。

    2.空间复杂度

    最差情况下1+...+N-1=(N-1)N/2,因此为O(N²)。

    3. 空间复杂度

    只需要几个有限的变量空间,所以是O(1)。

    4. 稳定性

    相等的不交换,只有大于时交换,就可以保证稳定性。

    三、选择排序

    • 时间复杂度O(N^2)
    • 额外空间复杂度O(1)
    • 是否可实现稳定性:否

    1. 原理

    每次从无序区中选择最小的数加入到有序区的末尾。与相应位置的数据进行交换

    2.空间复杂度

    每次遍历找最小值:N+N-1+....+1=N(N+1)/2,因此为O(N²)。

    3. 空间复杂度

    只需要几个有限的变量空间,所以是O(1)。

    四、快速排序

    • 时间复杂度O(N*logN)
    • 额外空间复杂度必须为O(logN)
    • 是否可实现稳定性:额外空间复杂度为O(logN)的情况下不可以,增加额外空间可以,但快排要求的就是空间复杂度为O(logN)。

    logN默认以2为底,代表长度为N的数组可以二分多少次。

    1、求M([3,1,5,4,2])中不在N([9,2,7])中的数的集合,即求M-N的差集

    (1)暴力搜索

    将M中每个数依次与N中的数进行遍历对比,不在N中就打印,在N中就直接跳过。
    时间复杂度O(MN)。

    (2)先排序再二分
    • N先排序,M中每个数通过二分搜索的方式确定有没有。
    • 排序时间复杂度为O(A),二分搜索时间复杂度为O(MlogN)。该方法的总时间复杂度为O(A)+O(MlogN),化简后为其中的大者。
    • L+(R-L)/2 = L+(R-L)>>1,位运算比除法快

    2、随机快排原理

    • 经典快排:将小于等于划分为一个区域。
    • 改进快排:将小于和等于分为两个区域。随机找一个数作为基准,小于这个数的放左边,等于的放中间,大于的放右边,然后再递归将左右边进行排序
    3、代码实现

    (1)如果为null或者length<2,直接返回
    (2)随机选择一个数作为基准数,交换至数组末尾
    (3)partition返回等于基准数的左右边界下标,再递归的对小于区和大于区进行快排。

    4、时间复杂度

    (1)选一个随机的划分数,为O(1)。
    (2)partition过程,将数组划分为三个区间,就是遍历一次数组的过程为O(N)。
    (3)左侧和右侧分别进行了递归。

    • 最差情况下,每次O(N)只能搞定一个数(1,2,3,4,5,6选择第一个数为划分数),最终复杂度为O(N²)。因此通过随机选择划分值可以在概率上尽量避免出现最差的情况。
    • 最好情况下,左右差不多都是N/2,2T(N/2),用master公式计算,a=b=2,d=1,因此T(N)=N*logN。master公式只能用于递归规模一样的情况。
    • 概率累加得到的期望值也为N*logN。

    5、空间复杂度

    因为左右需要递归排序,过程中需要不断记录划分值的位置,因此空间复杂度为断点树的高度,即log(2)(N)

    五、归并排序

    • 时间复杂度O(N*logN)
    • 额外空间复杂度O(N)
    • 实现可以做到稳定性

    1、原理

    • 递归法:将数组二分直至不能划分,即每个组都只有一个数值,然后不断合并排序。
    • 迭代法:room=1、2、4、8....不足就保持不变,不断排序合并

    2、小和问题

    • 问题:将每个数的左边所有比它小的数进行累加,得到的和为小和。
    • 思路:对每个数而言,只需要知道我的右边有多少个数比我大,我就累加几次。通过归并排序,在merge的的过程中,会依次遇到所有右边比我 大的数。

    3、求逆序对

    • 问题:存在多少对数对,数对中前面的数比后面的数大。
    • 思路:和小和问题同理,即对每个数而言,只需要知道我的右边有多少个数比我小,在我这个位置就产生了多少对逆序对。

    六、堆排序

    • 时间复杂度O(N*logN)
    • 额外空间复杂度O(1)
    • 实现不能做到稳定性

    关键步骤:heapInsert, heapify,堆的扩大和缩小操作

    1、堆的概念

    • 完全二叉树:叶节点只能出现在最下层和次下层,并且最下面一层的结点都集中在该层最左边的若干位置的二叉树。
    • 平衡二叉树(AVL树):它是一棵空树或它的左右两个子树的高度差的绝对值不超过1,并且左右两个子树都是一棵平衡二叉树,同时,平衡二叉树必定是二叉搜索树,反之则不一定。
    • (算法上的堆,不是系统中的堆)就是一个完全二叉树,可以把一个数组在逻辑上对应成一颗完全二叉树,左孩子2i+1,右孩子2i+2,父(i-1)/2。
    • 大根堆:一定是一个完全二叉树结构,任何一个根节点都是其所在数树(子树)中的最大值。

    2、原理

    (1)将数组变换为大根堆。每个数和父节点的值进行比较,如果大于父节点,就交换。时间复杂度=log1+log2+log3+...+logN=O(N)。
    (2)此时根节点的值最大,然后交换根节点和最后一个节点,保持最后一个节点不动,size--,超过size就代表越界。然后再构建大根堆,以此往复,每次找打最大值,直至所有数据排完序。

    最重要的heapinsert(初始化构建大根堆),heapify(下沉函数),size。优先级队列的题目就是堆,建立堆的过程和任何一个堆沉下去的过程重要。

    4、时间复杂度

    (1)建立大根堆的过程为O(N)。
    (2)heapify的过程就是树的高度,为O(logN)。
    (3)N个数的时间复杂度就是O(NlogN),但常数项很大。

    5、堆排和快排对比

    • 空间复杂度:堆排的空间复杂度可以做到O(1)是因为它的父子节点是通过公式的方式找到的,而快排需要记录断点的位置。
    • 时间复杂度:堆排的常数项很大,不存在最好和最坏情况;快排的常数项小。

    七、希尔排序

    插入排序,就是步长为1的希尔排序。

    八、系统中的排序原理

    • 数据量<60,插入排序,复杂度高,常数项少;
    • 数据量大的情况下,快速排序。

    不基于比较的的排序

    不基于比较的排序是有瓶颈的,对数据的位数和范围有限制。

    桶排序

    桶排序只是一个概念,基数排序和计数排序是桶排序的具体实现。

    一、计数排序

    先准备好相应数量的桶,然后遍历数据,将其放入对应的桶中,最后将桶中的数依次倒出来。

    二、基数排序

    1. 取位数最多的记录,其他不足该位数的补0。
    2. 按照个位数字依次放入桶中。
    3. 从0号桶开始,依次倒出来。先入桶的先倒(队列结构)。
    4. 按照十位数进桶,再倒出来,如此反复,直到最高位,结束。

    三、扩展

    1、求排序后的最大相邻数差值

    相关文章

      网友评论

          本文标题:排序算法总结

          本文链接:https://www.haomeiwen.com/subject/qinfvxtx.html