简单记录学习极客时间-数据与结构之美-排序相关的知识记录
tips
排序动画演示 可用手机app
时间复杂度 On2
冒泡排序
冒泡排序只会操作相邻的两个数据。每次冒泡操作都会对相邻的两个元素进行比较,看是否 满足大小关系要求。如果不满足就让它俩互换。一次冒泡会让至少一个元素移动到它应该在 的位置,重复 n 次,就完成了 n 个数据的排序工作。
冒泡排序最不容易出错。
1 | const arr = [4,5,2,3,8,7,6,1,10] |
插入排序
首先,我们将数组中的数据分为两个区间,已排序区间和未排序区间。初始已排序区间只有 一个元素,就是数组的第一个元素。插入算法的核心思想是取未排序区间中的元素,在已排 序区间中找到合适的插入位置将其插入,并保证已排序区间数据一直有序。重复这个过程, 直到未排序区间中元素为空,算法结束。
插入排序写起来比较费劲,要注意的地方比较多
我自己的写的
1 | function insert(arr) { |
教程写的,已经换成JavaScript,并加了注释
1 | function insert2(arr) { |
选择排序
1 | function select(arr) { |
时间复杂度 O nlogn
归并排序(稳定)
自己的代码,未优化的版本,找了好久的错误
1 | // 归并排序 |
比较大众的写法,这个类似于二叉树的后续遍历
1 | function mergeInto(arr, l, r) { |
快速排序(不稳定)
把比参考位置小的数移到左侧,大的移到右侧,如果不额外占用空间的话,比较难写
空间复杂度 O n 比较容易写出来
1 | function fastSort(arr, l, r) { |
O1空间复杂度左右归类的算法解释
1 | function fastSortHandle(arr, l, r) { |
当选择一个随机数N,下标INDEX作为参照点的话,程序运行到关键点的情况应该如下图所示
左侧是小于等于N的数,右侧是大于N的数,但是N可能在左侧或者右侧
最好的情况就是 把N的位置放进 j-1 和 j之间,但是这样时间复杂度较高,采取交换替代
如果N在左侧就和J-1
交换,N在右侧就和J
置换,如果不需要随机选择位置的话,N取最右侧元素,就不需要判断了
1 | if (rdm < j) { |
更好的随机数左右归类
力扣官方采取先用随机数与最后一位交换位置在进行常规方法,方法容易理解,也不容易出错
1 | int partition(vector<int>& nums, int l, int r) { |
最后总结出JavaScript版本的快速排序
1 | function fastSort(arr, l, r) { |
归并排序和快速排序的区别与联系
快排和归并用的都是分治思想,递推公式和递归代码也非常相似,那它们的区别在哪里呢?
可以发现,归并排序的处理过程是由下到上的,先处理子问题,然后再合并。
而快排正好相反,它的处理过程是由上到下的,先分区,然后再处理子问题。
归并排序虽然是稳定的、时 间复杂度为 O(nlogn) 的排序算法,但是它是非原地排序算法。我们前面讲过,归并之所以 是非原地排序算法,主要原因是合并函数无法在原地执行。快速排序通过设计巧妙的原地分区函数,可以实现原地排序,解决了归并排序占用太多内存的问题。