快速排序

参考http://www.cnblogs.com/ahalei/p/3568434.html

参考文章的解释非常清晰,不过代码写的有一点点问题,我稍微改一改,记录一下

大概的操作步骤是:先找一个基准数(下面的代码是直接把左边第一个作为基准),然后从左起第一个和右起第一个开始找,找什么呢,左边开始的那个找比基准大的,右边开始的那个找比基准小的,找到以后这俩互相交换位置,然后继续找,直到这俩相遇,相遇以后,把相遇点的这个数和基准交换,这就把原来的数组按照基准分成了左边全部是小于基准的和右边全是大于基准的,然后再对左边和右边分别进行迭代

function quicksort(arr, left, right) {
    var i, j, t, temp;
    if (left >= right)//这里我加了个=
        return;

    temp = arr[left]; //temp中存的就是基准数
    i = left;
    j = right;
    while (i != j) {
        //顺序很重要,要先从右边开始找,找小于temp的
        while (arr[j] >= temp && i < j)
            j--;
        //再找左边的,找大于temp的
        while (arr[i] <= temp && i < j)
            i++;
        //交换两个数在数组中的位置
        if (i < j) {
            t = arr[i];
            arr[i] = arr[j];
            arr[j] = t;
        }
    }
    //最终将基准数归位
    arr[left] = arr[i];
    arr[i] = temp;

    console.log(arr);
    quicksort(arr, left, i - 1);//继续处理左边的
    quicksort(arr, i + 1, right);//继续处理右边的
}


quicksort([5, 9, 3, 4, 6], 0, 4);//这里我从0开始找

参考文章里提到的关于一定要从哪边开始查找,找到了对应的解释,

关注点在相遇的时候,从左侧先走相遇的时候可以保证左边的都是小于标准值的,但是相遇时候的值是要大于标准值(或者已经走到最右边都没有大于标准值的),从右侧走正好是右边都是大于标准值的,但是相遇的时候值是小于标准值(或者走到 最左边都没有小于标准值)。所以要从基数的对面开始。两种情况,就看你从哪开始扫描。不一定非要从右边开始哦!也可以从左边的,那标准值就选右边的。

参考文章2 https://blog.youkuaiyun.com/insistgogo/article/details/7785038

快速排序使用分治的思想,通过一趟排序将待排序列分割成两部分,其中一部分记录的关键字均比另一部分记录的关键字小。之后分别对这两部分记录继续进行排序,以达到整个序列有序的目的。

感觉这句说的很好,我现在才知道分治原来是这个意思

这篇里写了比较详细的三种快速排序以及优化

参考文章3 http://www.cnblogs.com/hapjin/p/5518922.html

以下都是引用

相比于直接插入排序,快排合适于数据量大(上百万)的情形,而插入排序适合于小数据量的情形。因为,在数据量小的情形下,快排的递归是需要一定的开销的。

相比于归并排序,归并排序的比较次数要比快速排序少,但是它需要一个额外的临时数组,而且移动的元素多。而快速排序不需要显示地声明一个临时数组,它用的是递归栈。在C++中使用它来作为标准的排序程序,而JAVA中则是用归并排序来作为标准的排序(比如java.util.Arrays.java 中的sort(T[]) 方法使用的就是归并排序)。

快速排序主要有两个基本操作:一是选取枢轴元素,另一个则是递归分割数组。枢轴元素的选取对快速排序的效率至关重要,因为它决定了递归的深度。如果枢轴元素刚好处于数组的中间值,那么,数组在分割时就分成了平均的两部分。这样的递归的效率就好。如果每次选取的枢轴元素都是最大/最小 的那个元素,则快排复杂度达到了O(N^2),而且还用了递归栈空间。

枢轴元素的选取可以采用三数取中法。所谓三数取中法,即给定一个数组,选取数组中的第一个元素,最后一个元素,和中间那个元素。哪个元素的值位于中间,则它作为枢轴元素。比如,a[0]=5 , a[4]=1, a[9]=10, 那么a[4]<a[0]<a[9],故a[0]是枢轴元素。

采用三数取中法时,会有一个问题,就是当数组不断的递归划分变小之后,枢轴左边的元素个数不足3,这样三数取中法就失去了应有的意义。此外,正如前面提到,对于小数组而言,插入排序反而比快速排序的效率更高。

正是基于以上两个原因,我们可以将快速排序与插入排序结合。当递归划分的数组变小之后,达到某个值(CUTOFF)时,采用插入排序

function swapReference(arr, from, to){
   var tmp = arr[from];
    arr[from] = arr[to];
    arr[to] = tmp;
}

function media3(arr,left,right) {            
    var center = Math.ceil((left + right) / 2);
    if (arr[center] < arr[left])
        swapReference(arr, center, left);
    if (arr[right] < arr[left])
        swapReference(arr, right, left);

    // 最小的元素被放置在arr[left],然后再比较中间与最右的元素
    // 将最大的元素放在arr[right],而arr[center]存放中间元素(pivot)
    if (arr[right] < arr[center])
        swapReference(arr, right, center);

    swapReference(arr, center, right - 1);//将枢轴元素放在 arr[right-1]上.便于快排交换元素
    return arr[right - 1];
}

function quickSort(arr,left,right) {
    if(left + 1 < right){//这里的1其实就是cutoff
        var pivot = media3(arr, left, right);

        var i = left, j = right - 1;
        for(;;)
        {
            while(arr[++i] < pivot){}
            while(arr[--j] > pivot){}
            if(i < j)
                swapReference(arr, i, j);
            else
                break;
        }
        swapReference(arr, i, right - 1);
        console.log(arr);
        quickSort(arr, left, i - 1);
        quickSort(arr, i + 1, right);
    }else{//这里可以用插入排序
        if (arr[right] < arr[left]){
            swapReference(arr, right, left);
        }
        console.log(arr);
    }
}
var tar = [5,9,3,4,6,8,2,11];
console.log(quickSort(tar, 0, tar.length));
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值