写这篇文章源于之前的一次面试以及网上看到各种说原生的sort比快排快的例子,因为他们都没有写好快排。面试的时候让我写一个快排,我写出了我在网上看的的很简洁的一段代码(后来发现这个代码在数据结构和算法JavaScript描述这本书上也有):
function quickSort(arr){
if(arr.length < 1){
return [];
}
var left = [],right = [],flag = arr[0];
for(var i=1;i<arr.length;i++){
if(arr[i] <= flag){
left.push(arr[i]);
}else{
right.push(arr[i]);
}
}
return quickSort(left).concat(flag,quickSort(right));
}
这样写的话,乍一看确实是快排的思想,把比主元小的元素放到左数组,把比主元大的元素放到右数组,然后分别对左右数组的元素进行排序最终拼接成新的数组。
但是计算机课程里讲解快排的时候都不是这样讲解的,一趟快速排序的算法一般是这样讲解的:
- 设置两个变量i、j,排序开始的时候:i=0,j=N-1;
- 以第一个数组元素作为关键数据,赋值给key,即key=A[0];
- 从j开始向前搜索,即由后开始向前搜索(j--),找到第一个小于key的值A[j],将A[j]和A[i]互换;
- 从i开始向后搜索,即由前开始向后搜索(i++),找到第一个大于key的A[i],将A[i]和A[j]互换;
- 重复第3、4步,直到i=j;
采用js实现的版本如下:
function quickSort_two(arr){
function sort(start,end){
if(start + 1 > end){
return;
}
var flag = arr[start],f = start,l = end;
while(f < l){
while(f < l && arr[l] > flag){
l--;
}
arr[f] = arr[l];
while(f < l && arr[f] <= flag){
f++;
}
arr[l] = arr[f];
}
arr[f] = flag;
sort(start,f-1);
sort(f+1,end);
}
sort(0,arr.length-1);
}
对比这两中快排的写法,在时间复杂度上都是O(nlogn),但是第二种写法使用了更少的空间,第一种写法的空间复杂度是O(nlogn),而第二种的空间复杂度是O(logn),而且对数组的操作都在原数组上进行,减去了创建空间的消耗和时间,在性能上无疑有了更多的提升。
下面是三种排序算法的一个对比:
function quickSort_one(arr){
if(arr.length < 1){
return [];
}
var left = [],right = [],flag = arr[0];
for(var i=1;i<arr.length;i++){
if(arr[i] <= flag){
left.push(arr[i]);
}else{
right.push(arr[i]);
}
}
return quickSort_one(left).concat(flag,quickSort_one(right));
}
function quickSort_two(arr){
function sort(start,end){
if(start + 1 > end){
return;
}
var flag = arr[start],f = start,l = end;
while(f < l){
while(f < l && arr[l] > flag){
l--;
}
arr[f] = arr[l];
while(f < l && arr[f] <= flag){
f++;
}
arr[l] = arr[f];
}
arr[f] = flag;
sort(start,f-1);
sort(f+1,end);
}
sort(0,arr.length-1);
}
function quickSort_three(arr){
arr.sort(function(a,b){
return a-b;
});
}
function countTime(fn,arr){
var start = Date.now();
fn(arr);
var end = Date.now();
console.log(end - start);
}
function randomVal(num){
var arr = [];
for(var i=0;i<num;i++){
arr.push(Math.ceil(Math.random()*num));
}
return arr;
}
在chrome下的一个运行情况(以100000个数为例,由于每次排序后数组都发生了改变,所以每次都创建了新数组,以100000的基数来算的话,虽然不是同一个数组,但是结果也是值得参考的):
在firefox下的运行结果:
不论是firefox还是chrome,第一种排序算法的时间都是最长的,第二种是最快的,原生的sort方法比第二种方法稍微慢一点,但比第一种还是快多了。
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。