写这篇文章源于之前的一次面试以及网上看到各种说原生的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));
}

这样写的话,乍一看确实是快排的思想,把比主元小的元素放到左数组,把比主元大的元素放到右数组,然后分别对左右数组的元素进行排序最终拼接成新的数组。
但是计算机课程里讲解快排的时候都不是这样讲解的,一趟快速排序的算法一般是这样讲解的:

  1. 设置两个变量i、j,排序开始的时候:i=0,j=N-1;
  2. 以第一个数组元素作为关键数据,赋值给key,即key=A[0];
  3. 从j开始向前搜索,即由后开始向前搜索(j--),找到第一个小于key的值A[j],将A[j]和A[i]互换;
  4. 从i开始向后搜索,即由前开始向后搜索(i++),找到第一个大于key的A[i],将A[i]和A[j]互换;
  5. 重复第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方法比第二种方法稍微慢一点,但比第一种还是快多了。


yr1014
272 声望0 粉丝

下一篇 »
面试逻辑题