算法导论_第七章_快速排序

来源:互联网 发布:me域名多少钱 编辑:程序博客网 时间:2024/06/07 20:14

 算法导论_第七章_快速排序

快速排序的描述:

与归并排序一样,快速排序也使用了分治思想。

下面是对一个数组进行快速排序的三部分治过程:

1.分解:数组A[p,r]被划分为两个子数组A[p,q-1]和A[q+1,r],使得A[p,q-1]中的每一个元素都小于等于A[q],而A[q]也小于等于A[q+1,r];

2.解决:通过递归调用快速排序,对子数组进行排序。

3.合并:因为其是进行原址操作,所以其不需要合并

下面给出详细代码,介绍其具体运算过程:

/*************************************************************************> File Name: quicksort.cpp> Author:chudongfang > Mail:1149669942@qq.com > Created Time: 2016年06月28日 星期二 21时11分41秒 ************************************************************************/#include<iostream>#include<stdio.h>#include<string.h>#include<math.h>#include<stdlib.h>#include <algorithm>#define INF 0x3f3f3f3fusing namespace std;typedef long long ll;int partition(int A[],int p,int r);void quicksort(int A[],int p,int r);void swap(int *x,int *y){int t;  t=*x;  *x=*y;   *y=t; }int main(int argc,char *argv[]){    int A[100]={5,4,3,2,1};    int n=5;    quicksort(A,0,n-1);    for(int i=0;i<n;i++)        printf("%3d",A[i]);    return 0;}void quicksort(int A[],int p,int r){    if(p>=r)   return;    int q=partition(A,p,r);    //根据主元的位置进行递归求解    quicksort(A,p,q-1);    quicksort(A,q+1,r);}int partition(int A[],int p,int r){    int x=A[r];//以这个元素为主元,即拿这个元素与其他元素比较    int i=p-1;//i为最左边元素减一    //其中,从p到i为小于等于x的子数组1,而从i+1到j为大于等于x的子数组2,    //从j+1到r-1表示的是还未进行分配的子数组    //而元素r为主元    for(int j=p;j<=r-1;j++)    {        if(A[j]<=x)        {            i++;//子数组1加入一个元素            //加一后的i大于等于x            swap(A+i,A+j);            //交换后其恢复原来“秩序”        }    }        swap(A+i+1,A+r);//把主元放到合适的位置。    return i+1;//返回主元的位置}

其实其核心就是分割数组

其把数组分为四个部分:

1.从p到i为小于等于x的子数组1,而

2.从i+1到j为大于等于x的子数组2,  

3.从j+1到r-1表示的是还未进行分配的子数组  

4.元素r为主元

最后把r放到数组1和数组2的中间位置

partition函数的时间复杂度为Θ(n)




快速排序的性能:


快速排序的运行时间依赖与划分是否平衡,如果划分是不平衡的,那么其性能就接近插

入排序了



最坏情况划分:


当划分产生两个子问题分别包含了n-1个元素和0个元素时,快速排序的最坏情况为

Θ(n^2)

其表达式为T(n)=T(n-1)+Θ(n)

利用主方法,其时间复杂度为Θ(n^2)


最好情况的划分


在最可能平衡的划分中,就是两个子数组长度相等或差1,这时,其T(n)=2*T(n/2)+Θ(n)


利用主方法  其时间复杂度为Θ(n*lg(n))



平衡的划分:


快速排序的平均运行时间更接近与其最好情况下的性能,而不是最坏情况下的性能。

假设算法为9:1划分,

利用一个递归树,表示,

T(n)=T(9*n/10)+T(n/10)+Θ(n)


其每层的代价和为cn

一共有log(10/9) n=Θ(lg(n))层


所以其时间复杂度为O(n*lg(n))




99:1仍然一样,其时间复杂度为O(n*lg(n)),,因为其层数为log(100/99) n=Θ(lg(n))层


其实,任何一种常数比例的划分都会产生深度为Θ(lg(n))层的递归树,其中每层代价都为


O(n),所以只要划分为常数比例的,其算法的运行时间为O(n*lg(n))



对于平均情况的直接观察:


当好和差的划分交替出现时,快速排序的时间复杂度与全是好的划分一样,仍然是


O(n*lg(n))。区别只是O的符号中隐含的常数因子要略大一些。





快速排序的随机化版本:

在这里不用对输入的数组进行随机排序,只需随机选取其主元就可以满足随机化算法

这样划分就可以满足随机条件


下面上代码:


/*************************************************************************> File Name: quicksort.cpp> Author:chudongfang > Mail:1149669942@qq.com > Created Time: 2016年06月28日 星期二 21时11分41秒 ************************************************************************/#include<iostream>#include<stdio.h>#include<string.h>#include<math.h>#include<stdlib.h>#include <algorithm>#define INF 0x3f3f3f3fusing namespace std;typedef long long ll;int partition_random(int A[],int p,int r);void quicksort_random(int A[],int p,int r);void swap(int *x,int *y){int t;  t=*x;  *x=*y;   *y=t; }int main(int argc,char *argv[]){    srand(time(0));      int A[100]={5,4,3,2,1};    int n=5;    quicksort_random(A,0,n-1);    for(int i=0;i<n;i++)        printf("%3d",A[i]);    return 0;}void quicksort_random(int A[],int p,int r){    if(p>=r)   return;    int q=partition_random(A,p,r);    //根据主元的位置进行递归求解    quicksort_random(A,p,q-1);    quicksort_random(A,q+1,r);}int partition_random(int A[],int p,int r){    int y=p+rand()%(r+1-p);//随机交换A[r]    swap(A+y,A+r);    int x=A[r];//以这个元素为主元,即拿这个元素与其他元素比较    int i=p-1;//i为最左边元素减一    //其中,从p到i为小于等于x的子数组1,而从i+1到j为大于等于x的子数组2,    //从j+1到r-1表示的是还未进行分配的子数组    //而元素r为主元    for(int j=p;j<=r-1;j++)    {        if(A[j]<=x)        {            i++;//子数组1加入一个元素            //加一后的i大于等于x            swap(A+i,A+j);            //交换后其恢复原来“秩序”        }    }        swap(A+i+1,A+r);//把主元放到合适的位置。    return i+1;//返回主元的位置}



快速排序分析:



最坏情况分析


T(n)=max(T(q)+T(n-q-1))+Θ(n)


不妨猜测T(n)<=c*n^2


代入得  T(n)<=max(c*q^2+c(n-q-1)^2)+Θ(n)


二次函数的上界为n^2-c(2*n-1)



又有:n^2-c(2*n-1)+Θ(n)<=c*n^2


可以选择一个c使得c(2*n-1)>Θ(n)

Θ(n)-c(2*n-1)<0




期望运行时间:



我们首先对数组A的各个元素重新进行命名,z1,z2,z3,,,,zn


代表第i大的元素。


另外还定义z[i][j]为{zi,zi+1,,,,,zj}的集合。



这里利用了指示器随机变量X[i][j]代表了{zi和zj进行比较}


然后对于一个集合z[i][j]有   2/(j-i+1)的概率比较


(这里只有在主元选取zi和zj时能够进行比较,因为如果选取


中间元素的话,就把两个分割就无法进行比较)


然后根据求和

i:   1=>n-1

j:    i+1=>n

sum+= 2/(j-i+1)


最后后得到E[x]=O(n*lg(n))


所以在随机算法和输入互异的情况下,快速算法的


期望运行时间为O(n*lg(n))



















































0 0
原创粉丝点击