快速排序

来源:互联网 发布:数控车螺纹编程 编辑:程序博客网 时间:2024/05/19 04:05

快速排序

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

基本思想:

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

算法的具体实现:

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

一趟排序的实现过程

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


初始状态:

排序次数 X 6 22 1 7 2 排序方向 基准数 i=0 ↑ ↑ j=5 X=5

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

排序次数 2 6 22 1 7 X 排序方向 基准数 1 i=0 ↑ ↑ j=4 ← X=5

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

排序次数 2 X 22 1 7 6 排序方向 基准数 2 i=1 ↑ ↑ j=4 → X=5

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

排序次数 2 1 22 X 7 6 排序方向 基准数 3 i=1 ↑ ↑ j=3 ← X=5

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

排序次数 2 1 X 22 7 6 排序方向 基准数 4 i=2 ↑ ↑ j=3 → X=5

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

排序次数 2 1 X 22 7 6 排序方向 基准数 5 i=2 ↑↑ j=2 ← X=5

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

2 1 5 22 7 6 i=2 j=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中的表格之后才知道,什么才是真正难用……

原创粉丝点击