算法_八大内部排序算法

交换排序

1.冒泡排序

  1. 基本思想:每次根据大小比较相邻的两项,将最小或最大的数据慢慢浮到数组末尾。
  2. 复杂度分析:对数组中的n个数据项,第一趟有n-1次排序,第二趟有n-2次排序,以此类推,共有n*(n-1)/2次。可以认为冒泡排序需要O(N^2)的时间复杂度。
  3. 代码及优化

2.快速排序 😄

  1. 基本思想:采用分治策略,选择一个基准数作为分界点,使左边的数小于基准数,右边的数大于基准数;之后分别对左边和右边的数进行此操作。
  2. 复杂度分析:跳跃式交换,最坏时间复杂度同冒泡,为O(N^2),平均时间复杂度为O (NlogN)。快速排序仅在平均情况下优于冒泡排序。
  3. 代码及优化

选择排序

1.直接选择排序

  1. 基本思想:每次从未排序的队列中找到最值,放在已排序队列的末尾,这里即将最值与未排序的首位交换。
  2. 复杂度分析:选择排序和冒泡排序进行了相同的比较次数,都是n*(n-1)/2次,但是其交换次数较少。对于n值较大时,比较次数更重要,故选择排序需要O(N^2)的时间复杂度。
  3. 当n值较小时,选择排序比冒泡排序快,因为它进行交换的次数少。
  4. 代码及优化

2.堆排序 😄

  1. 简介:堆排序是基于完全二叉树这种数据结构实现的排序算法。简便起见,完全二叉树使用数组存储,可以根据索引快速定位元素。
    • 满二叉树:所有内部节点都有左右子节点,最后一层为叶子节点。
    • 完全二叉树:其每一个节点都与深度为k的满二叉树中编号从1到n的节点一一对应,即只允许最后一层有空缺节点,且空缺在右边。
    • 最大堆要求节点的元素都不小于其子节点,最小堆要求节点的元素都不大于其子节点。
  2. 基本思想:以最大堆为例,首先将待排序数组通过交换构造一个大顶堆,最后根节点一定是最大的数,然后将根节点的数与数组的最后一个数交换,即每次建堆并交换后仍保证剩余的数满足完全二叉树。然后将剩余的n-1个节点重新建堆并交换,直至有序。
  3. 复杂度分析:时间复杂度在三种情况下均为O(NlogN)。
  4. 代码示例

归并排序

1.合并排序 😄

  1. 基本思想:采用分治策略将待排序元素分成大小大致相同的两个子集合,分别对两个子集合进行排序,最终将子集合合并成要求的集合。
  2. 复杂度分析:采用递归的方式时间复杂度为O(NlogN),如果是数据有序的极端情况,采用自然合并排序需要O(N)时间。
  3. 代码详解

插入排序

1.直接插入排序

  1. 基本思想:将一个数插入到有序的数组中,得到一个仍然有序的数组。
  2. 复杂度分析:时间复杂度为O(N^2),适用于少量数据的排序。复杂度等同冒泡排序。

2.希尔排序

  1. 基本思想:插入排序的升级,将一个序列通过增量划分为多个序列,对每个序列进行插入排序,然后再通过更小的增量划分序列并排序,直到增量为1,此时即为直接插入排序。
  2. 复杂度分析:通过多次划分排序使得宏观上此序列已经有序,相对于插入排序减少了数据移动的次数。
  3. 代码示例

分配排序

1.基数排序(桶排序)

  1. 基本思想:采用分治策略,将一个数据表分割成许多桶,然后对各个桶里面的数据进行排序,最后遍历桶将数据放回原始数组。
  2. 复杂度分析:桶排序的时间复杂度为 O(M+N),M为桶的个数,为待排序数的个数。
  3. 代码及优化

总结

  1. 冒泡排序,直接插入排序,归并排序和基数排序是稳定的排序,即对于相同的数不会做改变其相对位置的操作。
  2. 相对于其他排序,堆排序和归并排序在三种情况下有较好的时间复杂度,都是O(Nlog2N)。但是归并排序需要相对更多的辅助空间。
  3. 实际使用需要结合多个排序方法进行优化,比如Arrays.sort()源码中,对于基本数据类型采用的是 Dual-Pivot Quicksort(即双基准快速排序算法/三向切分),它在大多数情况下能保持nlogn的时间复杂度。对于对象类型采用的就是归并排序的优化版本TimSort,当待排序的数据少于7时使用插入排序。
posted @ 2018-08-11 00:39  bkycrab  阅读(200)  评论(0编辑  收藏  举报