介绍
虽然叫它快速排序,但实际上,它的实现思路和上一章讲的冒泡排序并没有多大差别。
一句话总结思路:给定一个数组,对于数组中的第一个数,如果你能把比它大的数都放在他后面,比它小的数放在它前面,那么你就能给这整个数组排序。
排序数组中的一个数
如存在数组[6, 1, 5, 2, 4, 9, 8],我需要把6放在正确的位置,比它大的数放在它后面,比它小的数放在它前面,下面是代码实现:
代码
function sortOne(source) {
if (source.length <= 1) return source;
let maxThanSelf = [];
let others = []
for (let i = 1; i < source.length; i++) {
if (source[i] > source[0]) {
maxThanSelf.push(source[i]);
} else {
others.push(source[i]);
}
}
return [...others, source[0], ...maxThanSelf];
}
代码的结构极其简单,找到比它大的数和其它数【等于或者小于它】,然后将它们组合在一起。
注意:这里的代码虽然很好理解,但是很浪费性能,你会发现中间创建了很多新数组,但这实际是不需要的,强烈建议去了解一下双指针版本【网上的代码快排代码会告诉你用双指针要怎么写】
下一步
- 还是以
[6, 1, 5, 2, 4, 9, 8]为例,把6排好后,数组会变为[1, 5, 2, 4, 6, 9, 8],或者说,我们可以把数组分为3部分,分别是[1, 5, 2, 4]【比6小】,6,[9, 8]【比6大】。 - 以
[1, 5, 2, 4]为例,我们同样可以通过刚才的方式,把1放在正确的位置,又会出现3个部分:[],1,[5, 2, 4]。此时原数组将存在5个部分,分别是[],1,[5, 2, 4],6,[9, 8]
- 可以发现,只要我们对数组长度大于1的部分,重复这个操作,我们就可以把数组细分,直到分为长度为1或长度为0,最后只要把所有部分再按照顺序连接起来,整个数组就排好了!
代码
其实我们只需要修改sortOne的一行代码即可
function sort(source) {
if (source.length <= 1) return source;
let maxThanSelf = [];
let others = []
for (let i = 1; i < source.length; i++) {
if (source[i] > source[0]) {
maxThanSelf.push(source[i]);
} else {
others.push(source[i]);
}
}
// 只需要在这里,递归的去排序就行
return [...sort(others), source[0], ...sort(maxThanSelf)];
}
注意: 这里和sortOne存在一样的问题,会频繁的创建新数组,修改方法和sortOne一样,将sortOne部分改成双指针版本,交换数据,而不是创建新数组
ps:
- 当然,这里选择的是排好第一个数,实际上你可以选取数组中的任意一个数
时间复杂度
- 对于数据量n,把一个数的位置排好,复杂度是n
- 排好一次后,数组被拆成3份,对这3份数据【实际上是2份,比它大(假设数量是m)和比它小(假设数量是k)的部分】,再分别重复上述操作,加起来的复杂度同样是n【m + k】
- 那我们需要拆分多少次呢?
- 最好的情况,第一个数的位置非常好,每次都能将数组平分,那我们需要拆log(以2为底)n次,如1024个数据,需要拆10次【理论上】
- 最坏的情况,第一个数的位置非常糟糕,那拆了等于没拆【每次拆只拆了一个数据】,需要拆n-1次
所以,快速排序的算法复杂度是,最好O(nlogn),最差O(n2)
下一章
归并排序