快速排序

Posted algorithm-process

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了快速排序相关的知识,希望对你有一定的参考价值。

  快速排序为什么被称之为快速排序呢?从字面意思上来看肯定是因为它比较快啦。当然实际上也是这样,相比于其他的排序算法,它的平均的时间复杂度为O(nlogn),这可以说是很快的一种排序算法了。当然在某些情况下,也会出现比其他排序算法慢的情形,所以没有什么最好的排序算法,只有最合适的排序算法。所以在平常应用的时候要根据实际情况来选择合适的排序算法。

  拿为什么快排比较快呢?因为它每次进行了划分,选择一个基准值后,将这个待排序序列分成了小于基准值的一半和大于基准值的一半,然后继续进行相同的操作,知道最后的一半只剩下一个元素。

  对快速排序算法的描述:

    1.分解:数组A[p...r]被划分为连个子数组A[p...q-1]和A[q+1...r],使得A[p...q-1]中所有的数都比A[q]小,A[q+1...r]中所有的数都比A[q]大;

    2.解决:通过递归调用快速排序,对子数组A[p...q-1]和A[q+1...r]进行排序;

    3.合并:因为数组原址排序,所进行完之后数组A[p...r]已经有序。

  代码如下:

1 quicksort(int a[],int l,int r){//快排递归形式 
2     if(l<r){
3         int q=partition(a,l,r);//找到中间数,不一定是中位数 
4         quicksort(a,l,q-1);
5         quicksort(a,q+1,r);
6     }
7 }

   所以接下就是如何解决划分(partition)的问题了。有三种方法可以来解决划分的问题:一遍单向扫描法,双向扫描法和三分法,接下来我们就来好好看看这三种方法。

 一遍单向扫描法

  思路:首先找到一个基准值p,使用两个指针(sp,bigger)将待排序的数组分成三个部分,sp左边的表示全部小于基准值的,bigger右边的部分表示全部大于基准值的,sp和bigger之间的部分为待排序的部分。

  当sp元素小于p时,交换sp和bigger的元素,然后bigger--。

技术图片

  代码如下:

 1 int partition(int a[],int l,int r){
 2     int x=a[l];//基准值
 3     int sp=l+1,bigger=r;//选定两个指针
 4     while(sp<=bigger){
 5         if(a[sp]<=x) ++sp;//当扫面元素小于基准值时,右移扫描指针 
 6         else {//当扫描元素大于基准值时,两个指针的元素交换,然后左移右侧指针 
 7             swap(a[sp],a[bigger]);
 8             --bigger;
 9         }
10     } 
11     swap(a[l],a[bigger]);//最后基准值与bigger指针元素交换,得到 
12     return bigger;//得到中间值 
13 }

 

  一遍单向扫描只能从一个放向走到底,这样看起来是不是也很慢呢?如果我们让这两个指针进行相对的移动是不是更快了!!这就是我们接下来要介绍的双向扫描法。

 双向扫描法

  思路:和上面的一遍单向扫描法一样也是设置两个指针分别为左指针sp,右指针bigger,最终得到的结果也是sp左边的元素都比p小,bigger右边的元素都比p大。那和一遍单向扫描法的区别在于sp和bigger指针同时相向移动,当sp元素大于p且bigger元素大于p时,两个指针所指的元素交换位置,然后继续移动指针,直到到达边界条件。

  技术图片

    代码如下:

 1 int partition(int a[],int l,int r){
 2     int x=a[l];//基准值
 3     int p=l+1,q=r;
 4     while(p<=q){
 5         //记住得加上边界条件,因为在移动指针的过程中可能会越界  
 6         while(a[p]<=x&&p<=q) ++p;//sp元素小于p则右移
 7         while(a[q]>x&&p<=q) --q;//bigger元素大于p则左移
 8         if(p<=q) swap(a[p],a[q]);//交换两指针元素
 9      }
10        //此时bigger在sp左边且bigger所指元素是小于p的
11      swap(a[l],a[q]);//最后还得与基准值交换
12      return q;
13 }

 

  最后还有一种较为特殊的情况,当待排序序列中的重复元素较多时,我们可以使用一个equal指针来指向与基准值相等的元素,然后返回相等序列的头部和尾部。接下来我们就介绍下这个方法。

 三分法

  思路:1.选定基准值,
             2.使用三个指针,一个扫描指针sp,一个右指针bigger,一个相等指针equel
             3.当sp小于基准值时,equel元素与sp元素交换,然后equel,sp右移,
                当sp等于基准值时,sp右移
                当sp元素大于基准值时,sp元素与bigger元素交换,bigger左移
             4.最后equel-1元素与基准值交换,返回equel-1和bigger

   技术图片

    代码如下:

 1 #include<iostream>
 2 #include<algorithm> 
 3 using namespace std;
 4 
 5 struct equal {//使用一个结构体来存储中间相等区间的左边界和右边界 
 6     int right;
 7     int left;
 8 }equal1;
 9 
10 void  partition(int a[],int l,int r){
11     int x=a[l];
12     int sp=l+1,e=l+1,bigger=r;
13     while(sp<=bigger){
14         if(a[sp]<x) {
15             swap(a[e],a[sp]);
16             ++e;
17             ++sp;
18         }
19         else if(a[sp]==x) ++sp;
20         else {
21             swap(a[bigger],a[sp]);
22             --bigger;
23         }
24     }
25     swap(a[l],a[e-1]);
26     equal1.right=bigger;
27     equal1.left=e-1;
28 }
29 
30 quicksort(int a[],int l,int r){//快排递归形式 
31     if(l<r){
32         partition(a,l,r);//找到中间数,不一定是中位数 
33         quicksort(a,l,equal1.left-1);
34         quicksort(a,equal1.right+1,r);
35     }
36 }
37 int main(){
38     int a[10]={4,6,4,2,8,0,1,4,5,4};//测试数据
39     quicksort(a,0,9);
40     for(int i=0;i<10;i++) cout<<a[i]<<" ";
41     cout<<endl; 
42     return 0;
43 } 

  以上就是快排的三种基本方法,可以根据实际情况来选择使用哪中划分方法。欢迎大家指正哦~,如果喜欢的话,点个关注呗(●‘?‘●)。

 

 

 

 

 

以上是关于快速排序的主要内容,如果未能解决你的问题,请参考以下文章

算法排序之堆排序

前端开发工具vscode如何快速生成代码片段

前端开发工具vscode如何快速生成代码片段

如何使用sublime代码片段快速输入PHP头部版本声明

代码片段如何使用CSS来快速定义多彩光标

vs2003:快速片段工具