这里是传送门⇒总结:关于排序算法html
|
平均时间复杂度 |
最优时间复杂度 |
最差时间复杂度 |
空间复杂度 |
稳定性 |
快速排序 |
O(nlogn) |
O(nlogn) |
O(n2) |
O(logn) |
不稳定 |
快速排序是对冒泡排序的一种改进,是一种划分交换排序,采用了分治的思想算法
- 算法描述
- 从待排序列中选定一个基准数(好比序列的第一个数),把比基准数小的数放在左边,比基准数大的放在右边,同样大的放哪边都行(如下是让其在左边)。这个过程称为分区
- 而后再按照这种方式把左右两个区间再分别进行分区
- 以此类推...
- 整个排序过程能够递归进行(好像说递归有可能会爆栈)
- 分区的具体实现
- 总结起来就是“挖坑填数”
- 选定待排序列第一个数array[left]为基准数,把array[left]的值保存在变量baseVal中
- 因为array[left]的值已经被保存起来了,能够理解为array[left]的值被拿走了,如今array[left]上有一个坑,如今来找一个数填进来
- 这个坑在左边,因此从后向前扫描待排序列array,用变量j记录扫描位置
- 当发现此时的array[j]的值比基准值小或者等于基准数,把此时的array[j]的值填到坑里,也就是让a[left] = a[j]
- 如今的a[left]填完数了,但是a[j]的数被拿走填到a[left]了,如今变成a[j]有坑了,须要找另外一个数来填
- 这个时候坑在右边了,从头向后扫描待排序列array,用变量i记录扫描位置
- 当发现此时的array[i]的值比基准值大,把此时的array[i]的值填到坑里,也就是让a[j] = a[i]
- 如今的a[j]填完数了,但是a[i]的数被拿走填到a[j]了,如今变成a[i]有坑了,须要找另外一个数来填
- 以此类推...
- 直到i和j相遇了,即左右两边的坑都填完了,只剩下中间的坑
- 最后把一开始保存在baseVal的值填进最后一个坑
- 也就是在左边挖坑,去右边找一个应该在左边的数,填进坑里,这个行为致使右边有坑,因此得去左边找一个应该在右边的数来填,而后又致使左边有坑...最后剩一个坑,就把一开始保存的基准数填进去
- JS实现
// 此处传入的array会被直接改变
function QuickSort(array, left, right) {
if (left < right) {
var baseVal = array[left];
var i = left,
j = right;
while (i < j) {
while (i < j && array[j] > baseVal) {
j--;
}
if (i < j) {
array[i++] = array[j];
}
while (i < j && array[i] <= baseVal) {
i++;
}
if (i < j) {
array[j--] = array[i];
}
}
array[i] = baseVal;
QuickSort(array, left, i - 1);
QuickSort(array, i + 1, right);
}
}
- 分析
- 快速排序的一次分区是从两头交替寻找合适的数,直到i,j相遇,因此每一次分区的时间复杂度都是O(n),那么整个快速排序的时间复杂度与分区次数有关
- 最差状况是每次分区选择的基准数都是当前序列的最小值(即待排序列一开始就升序)的时候,这会致使每次分区后左区间的长度为0,(其实就至关于冒泡了,每次只排好一个数)这种状况下,长度为n的待排序列将进行n-1次分区,那么最差时间复杂度为O(n2)
- 最优状况是每次分区选择的基准数刚好将当前序列几乎等分,这种状况下,长度为n的待排序列将进行大概log2n次分区,那么最优时间复杂度为O(nlogn)
- 尽管快速排序只须要O(1)大小的辅助空间,但考虑递归深度,快速排序须要一个栈空间来实现递归。最好的状况下,所需栈的最大深度为log2(n+1),最优空间复杂度为O(logn);但最坏的状况下,所需栈的最大深度为n,最差空间复杂度为O(n)。快速排序的平均空间复杂度为O(logn)
- 想知道怎么由递归算法时间复杂度公式计算时间复杂度的,看这个别人家的博客
- 或者是这种利用递归树来描述递归算法执行状况的分析方式,看另一个别人家的博客
- 因为键值的比较和交换是跳跃进行的,所以快速排序是一种不稳定的排序。假如以为把键值比较条件
array[i] <= baseVal
改为array[i] < baseVal
能够变成稳定排序的话,请看这个例子[3,4,4,2,6]
- 优化
- 最理想的状况是,选择的基准数刚好能把待排序列分红两个几乎等长的区间
- 而上面的方法是以待排序列的第一个数为基准数
- 咱们能够改变基准数的选择方案来优化这个算法
- 随机选取基准:取待排序列中任意一个元素做为基准
- 三数取中:使用左端、右端和中心位置上的三个元素的中值做为枢纽元
- 实现方法:按照上面的算法,只要让选择的基准和待排序列的第一个数字交换便可