快速排序

快速排序

名副其实,它在排序中以“快”而闻名。它是处理大数据最快的排序算法之一

基本思想:

通过一趟排序将待排记录分隔成独立的两部分,其中一部分记录的关键字均比另一部分的关键字小,则可分别对这两部分记录继续进行排序,以达到整个序列有序。

算法的具体实现:

虽然网上介绍快速排序的文章很多,但是具体怎么实现每一步的讲解却很少,对于大多数初学者很去难弄懂里面的关键。下面我来详细介绍一下每一步的实现过程。
快速排序分成两步,①一趟排序,②递归调用
数组:arr=[5,6,22,1,7,2];

一趟排序的实现过程

i表示左边“↑”的坐标,指左边下一次等待比较的位置
j表示右边“↑”的坐标,指右边下一次等待比较的位置
X表示基准数,当前我们选取左边第一个数5作为基准数。
目标:小于等于基准数的放在基准数的左边,大于基准数的放到基准数的右边。
完成条件:i>=j


初始状态:

排序次数X622172排序方向基准数
i=0j=5X=5

第一次:从右j开始进行比较,如果arr[j]<X=5,则与基准数的位置进行交换;
更新j的位置*。在这里是2与X进行了交换。

排序次数262217X排序方向基准数
1i=0j=4X=5

第二次:从左i开始进行比较,如果arr[i]>X=5,则与基准数的位置进行交换;
更新i的位置*。在这里是X与6进行了交换。

排序次数2X22176排序方向基准数
2i=1j=4X=5

第三次:从右j开始进行比较,如果arr[j]X=5,则与基准数的位置进行交换;
更新j的位置。在这里是1与X进行了交换。

排序次数2122X76排序方向基准数
3i=1j=3X=5

第四次:从左i开始进行比较,如果arr[i]>=X=5,则与基准数的位置进行交换;
更新i的位置。在这里是X与22进行了交换。

排序次数21X2276排序方向基准数
4i=2j=3X=5

第五次:从右j开始进行比较;更新j的位置,达成判断条件i>=j。

排序次数21X2276排序方向基准数
5i=2↑↑j=2X=5

将X替换成5,完成一趟排序*。
排序结果:

2152276
i=2j=2
注释*:

i和j位置的更新规则:
如果arr[i]<=X=5,则i++;重复上述过程,直到arr[i]>=X=5,或者是i>=j
如果arr[j]>X=5,则j--;重复上述过程,直到arr[j]<X=5,或者是i>=j

一趟排序中基准数(X)的替换规则:
由于基准数是需要不断被替换的,所以在实际代码中,我们并不需要反复地将基准数与目标数进行替换。
在这里我们采用了挖空填数法。即将把基准数放置到X中,这样的话基准数就可以所在的位置就可以随意被填充(挖坑)。
等到一趟排序结束之后,我们再将X放回到坑中(填数),这样就提高了代码的效率。

一趟排序的代码实现:
 function quickSort(a, begin, end) {
        //确保开始和结束的数正确
        var begin = typeof begin != 'number' ? 0 : begin,
            end = typeof end != 'number' ? a.length - 1 : end;
        if (end - begin <= 1) return; //结束循环的条件
        var x = a[begin], //默认选择第一个数的为基准数
            p1 = begin, //左边“↑”的位置
            p2 = end, //右边“↑”的位置
            flag, //本次比较是否发生数组交换
            dr = true; //比较的方向,默认true时为从右到左开始判断
        while (p1 < p2) {
            flag = false; //默认数组没有发生交换
            if (dr) {//从右到左开始判断
                for (var i = p2; i >= p1; i--) {
                    if (a[i] <= x) {//如果a[i]小于等于基准数
                        a[p1] = a[i]; // 则将a[i]赋给a[p1]
                        p1++; // 从左边下一个开始比较
                        p2 = i; // 从右边上一个开始比较
                        dr = !dr; //更改比较方向
                        flag = true; //本次发生了数组交换
                        break; //退出循环
                    }
                }
// 如果for循环没有发现满足条件的元素,则说明X的位置本身就是正确的,不需要改变
                if (!flag) { //如果没有发生数组交换,则说明一趟排序已经完成
                    p2 = p1; // 跳出while

                }
            } else {//从左到右开始判断      
                for (var j = p1; j < p2; j++) { 
                    if (a[j] > x) {
                        a[p2] = a[j]; 
                        p2--; 
                        p1 = j; 
                        dr = !dr; 
                        flag = true; 
                        break; 
                    }
                }  
                if (!flag) { 
                    p1 = p2;
                }
            }
        }
        a[p1] = x;//一趟排序已经完成,将基准数代入到a[p1]中
    }

递归调用

实现简述:

通过一趟排序,我们可以把数组分成两个部分,一部分小于等于基准数,另一部分大于基准数。前者我称为前基,后者我称为后基。
对于前基,我们也进行一趟排序,也可以分成两个部分……重复这个过程,直到前基每一个部分都只有一个数或者是为空,这就说明前基就已经排好;对于后基也是如此。

递归的实现:
function quickSort(a, begin, end){
    //前面代码不变
  a[p1] = x;//一趟排序已经完成,将基准数代入到a[p1]中
  //在上行代码后插入这两行代码
 quickSort(a,begin,p1-1);
  quickSort(a,p1+1,end);
}

参考资料:

[1]http://blog.csdn.net/morewindows/article/details/6684558 (非常好的文章,本文就是采用了“挖坑填数+分治法”的代码实现)

小尾巴吐槽:原本以为CSDN中html编辑器中的表格不好用(边框颜色和背景颜色重叠了),结果用了Markdown中的表格之后才知道,什么才是真正难用……

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值