快速排序算法的几种版本及实现

来源:互联网 发布:好的配音软件 编辑:程序博客网 时间:2024/05/16 13:32

写在前面的话

        最近系统地学习了快速排序算法,在此作一笔记。主要包括快排的各种版本:普通版本,改进的普通版本,随机化版本,三数值取中分割版本和Stooge版本。对各版本进行了简要分析,并给出了具体实现。

《算法导论》对快排的描述

        快速排序是基于分治模式的,下面是对一个典型子数组A[p..r]排序的分治过程的三个步骤:
        分解:数组A[p..r]被划分成两个(可能空)子数组A[p..q-1]和A[q+1..r],使得A[p..q-1]中的每个元素都小于等于A(q),而且,小于等于A[q+1..r]中的元素。下标q也在这个划分过程中计算。
        解决:通过递归调用快速排序,对子数组A[p..q-1]和A[q+1..r]排序。
        合并:因为两个子数组是就地排序的,它们的
合并不需要操作:整个数组A[p..r]已排序

        快速排序的关键是PARTITION(分解)过程,它对子数组A[p..r]进行就地重排。PARTITION结果的好坏直接影响快排的效率。

        下面用具体代码来分析各种版本的快排。首先是《算法导论》7.1节描述的,普通版的快排。

 #include"stdio.h" 

/*普通版快速排序*/

//函数声明

void QuickSort(int A[],int n);

void QSort(int A[],int l, int r);

int Partition(int A[],int l, int r);

 

int Partition(int A[],int p, int r) //分割过程

{

    int t;

    int x = A[r];

    int i = p - 1;

    for(int j = p; j < r; j++)

    {

        if(A[j] <= x)

        {

             i++;

             t = A[j]; A[j] = A[i]; A[i] = t;

        }

    }

    t = A[i+1]; A[i+1] = A[r]; A[r] = t;

    return i+1;

}

 

void QSort(int A[],int l, int r) //三个参数的快排子模块

{

    int p;

    if(l < r)

    {

        p = Partition(A, l, r);

        QSort(A, l, p-1);

        QSort(A, p+1, r);

    }

}

 

void QuickSort(int A[],int n)   //快排

{

    QSort(A, 0, n-1);

}

 

void main()    //验证排序

{

    int a[5]={6,2,4,8,5};

    QuickSort(a, 5);

 

    for(int i=0;i<5;i++)

    {

        printf("%d ",a[i]);

    }

    printf("\n");

 

}

    这个版本的PARTITION方法简单易懂,但有时会有一些不必要的交换发生,如排序 2 8 1 3 5 6 4

2 8 1 3 5 6 4
第一次交换8和1
2 1 8 3 5 6 4
第二次交换8和3
2 1 3 8 5 6 4
最后一次交换8和4
2 1 3 4 5 6 8
一次分割完成

    对PARTITION过程稍加修改,得到优化的分解过程:

int Partition(int A[], int l, int r) //稍加优化的分割过程

{

    int t;

    int x = A[r];

    int i = l-1, j = r;

    while(1)

    {

        while(A[++i] < x)

        ;

        while(A[--j] > x)

        ;

        if(i < j)

        {

             t = A[i]; A[i] = A[j]; A[j] = t;

        }

        else

             break;

    }

    t = A[i]; A[i] = A[r]; A[r] = t;

    return i;

}

   这一PARTITION过程通过比较,减少了交换次数。

    上面两种方法都把输入数组的最后一个元素作为主元,即哨兵元素。如果输入是随机的,那么这是可以接受的,但是如果输入是预排序的或是反序的,那么这样的主元就产生一个劣质的分割,因为所有元素不是都被划入A[p..q-1]就是都被划入A[q+1..r]。下面给出两种可以避免产生劣质分割的PARTITION过程。

/*随机分割法*/

int Partition(int A[],int p, int r)

{

    int t;

    int rd = rand() % (r-p+1) + p;

    assert(rd >= p && rd <= r);

 

    t = A[r]; A[r] = A[rd]; A[rd] = t;/*将主元放在位置r */

 

    int x = A[r];

    int i = p-1, j = r;

    while(1)

    {

        while(A[++i] < x)

        ;

        while(A[--j] > x)

        ;

        if(i < j)

        {

             t = A[i]; A[i] = A[j]; A[j] = t;

        }

        else

             break;

    }

    t = A[i]; A[i] = A[r]; A[r] = t;

    return i;

}

 

/*三数值取中分割法*/

int Partition(int A[],int p, int r)

{

    int t;

    int c = (p+r)/2;

    if(A[p] > A[c])

    {

        t = A[p]; A[p] = A[c]; A[c] = t;

    }

    if(A[p] > A[r])

    {

        t = A[p]; A[p] = A[r]; A[r] = t;

    }

    if(A[c] > A[r])

    {

        t = A[c]; A[c] = A[r]; A[r] = t;

    }

    /*使得A[l] <= A[c] <= A[r] */

 

    int k = r - 1;

    t = A[c]; A[c] = A[k]; A[k] = t; /*将主元放在r-1的位置*/

 

    int x = A[k];

    int i = p-1, j = k;

    while(1)

    {

        while(A[++i] < x)

        ;

        while(A[--j] > x)

        ;

        if(i < j)

        {

             t = A[i]; A[i] = A[j]; A[j] = t;

        }

        else

             break;

    }

    t = A[i]; A[i] = A[k]; A[k] = t;

    return i;

}

   另外,Howard、Fine教授提出了一种“漂亮的”排序算法,称为Stooge排序。它将待排序数组分为3段,递归地分别对输入数组的前2/3、后2/3和前2/3进行排序。这是一个号称很厉害的排序算法。说它厉害并不是因为它有多么的快,事实上它比插入排序还要慢。它的厉害之处在于,用一般的掰手指头的方法绝对无法证明它的正确性。关于其证明,详见http://apps.hi.baidu.com/share/detail/19522201。具体算法如下:

<

/*Stooge_sort Stooge排序 */ 

void stooge(int A[], int i, int j)

{

    if(A[i] > A[j])

    {// exchange

        int temp = A[i];

        A[i] = A[j];

        A[j] = temp;

    }

 

    if(i+1 >= j)

    return;

 

    int k = (j-i+1)/3;// first 2/3

    stooge(A, i, j-k);// last 2/3

    stooge(A, i+k, j);// first 2/3

    stooge(A, i, j-k);

}

 

int partition(int A[], int p, int r)

{

    int x = A[r];

    int i = p-1;

    for(int j=p; j<r; j++)

    {

        if(A[j] <= x)

        {

             i++;

             int temp = A[i];

             A[i] = A[j];

             A[j] = temp;

        }

    }

    int temp = A[i+1];

    A[i+1] = A[r];

    A[r] = temp;

    return i+1;

}

 

int main()

{

int A[] = {3,5,2,7,4,10,1,9};

 

for(int i=0;i<8;i++)

        printf("%d ",A[i]);

    printf("\n");

 

int x = partition(A, 0, 3);

 

int len =sizeof(A)/sizeof(int);

stooge(A, 0, len-1);

 

for(int i=0;i<8;i++)

        printf("%d ",A[i]);

    printf("\n");

 

return 0;

}

/p> 

        Stooge排序简单易懂,但遗憾的是其效率很低,因为递归和比较的次数太多。

 

 

 

 

 

原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 肾炎会引起脸肿怎么办 12小孩尿蛋白3是怎么办 肝癌介入手术后肝功能不好怎么办 屁多且臭便秘怎么办 肝癌术1年后复发怎么办 怀孕便秘怎么办或大便太干拉不出 肠鸣便秘怎么办多尿 奥司他韦过量怎么办 憋的时间长尿痛怎么办 手过敏了怎么办最简单 肾结石不痛但是有血尿怎么办 儿童医院血液科挂不到号怎么办 搬完重物手抖怎么办 弯腰搬重物腰疼怎么办 搬了重物后腰疼怎么办 例假不走公务员体检血尿怎么办 憋尿久了尿不出来怎么办 憋尿引起的总有尿意怎么办 如果孕妇憋尿了怎么办 孕妇憋尿半个月怎么办 尿憋久了排空后膀胱疼怎么办 胸疼肚子疼不规则流血怎么办 上小便下面会痛怎么办 两岁宝贝憋尿怎么办 打激素脸胖了怎么办 医生写的病历看不懂怎么办 怀孕尿蛋白3个加怎么办 两周岁宝宝牙齿坏掉怎么办 前列缐炎引起尿血怎么办 牙齿牙根长在神经里怎么办 牙齿杀完神经牙根发炎怎么办 牙齿有大洞好疼怎么办 大门牙有蛀牙了怎么办 大门牙蛀牙黑了怎么办 大门牙蛀牙有洞怎么办 大门牙的缝蛀了怎么办 最里面的牙烂了怎么办 牙齿蛀了个洞怎么办办 牙齿痛怎么办才能治好? 门牙牙齿蛀掉了怎么办 吃了辣的牙齿痛怎么办