***********快排改进

来源:互联网 发布:2017年网络语言 编辑:程序博客网 时间:2024/05/18 02:46

快速排序的改进

快速排序最坏情况下,要比较O(n^2)次,但平均性能为nlogn,基本达到了比较类排序所需时间的的下界。核心代码为:

void qSort(int *data, int begin, int end)

{

    int pivot, i, j;

    if(begin>=end)

        return;

    

    i = begin;

    j = end;

    pivot = data[i];

    while(i<j)

    {

        while(i<j && data[j]>=pivot)

            j--;

        data[i] = data[j];

        while(i<j && data[i]<=pivot)

            i++;

        data[j] = data[i];

    }

    data[i] = pivot;

    

    qSort(data, begin, i-1);

    qSort(data, i+1, end);

}

一种改进:对于一个已排好序的序列来说,插入排序应该是最快了,时间复杂度为O(n)。插入排序代码为:

void iSort(int *data, int begin, int end)

{

    int i,j,tmp;

    for(i=begin+1;i<=end;i++)

    {

        j = i-1;

        tmp = data[i];

        while(j>=begin && data[j]>tmp)

        {

            data[j+1] = data[j];

            j--;

        }

        data[j+1] = tmp;        

    }

    return;

}

 

由于快速排序算法在处理小规模数据集时表现的不是很好(可能是因为存在递归调用),因此在数据规模小到一定程度时,改用插入排序。另一方面考虑,对于大规模数据来说,前期采用的快速排序已经将一些元素放到了正确的位置上,因此当规模降低到一定程度时,可以认为一些元素已经基本有序了。对基本有序的序列进行排序,插入排序是最好的选择,不光比较次数少,还免去了递归调用的过程。因此,第一种改进方法为:

当数据规模小于一定程度时,改用插入排序。具体小到何种规模时,采用插入排序,这个理论上还不解,一些文章中说是5到25之间。SGI STL中的快速排序采用的值是10.

 

第二个有改进空间的地方是:中轴元素的选取。如果简单的只是选择第一个或最后一个作为中轴元素,这样当待排序序列基本有序的时候,它就退化为O(n^2)的时间复杂度。因此,第二中改进方法为:

可以取最左边、最右边、中间这三个位置的元素中的中间值作为中轴元素,来避免这种情况。

 

对于一个每个元素都完全相同的一个序列来讲,快速排序也会退化到O(n^2)。要将这种情况避免到,可以这样做:

在分区的时候,将序列分为3堆,一堆小于中轴元素,一堆等于中轴元素,一堆大于中轴元素,下次递归调用快速排序的时候,只需对小于和大于中轴元素的两堆数据进行排序,中间等于中轴元素的一堆已经放好。

 

总结一下,主要有3点有改进空间:

1 问题降到一定规模时,改用插入排序

2 中轴元素的选取

3 分成3堆,一方面避免相同元素这种情况,另一方面也可以降低子问题的规模。这个感觉有点想DFS中寻找剪枝条件来降低搜索规模一样。


从以上3点出来,完成新的快速排序,代码如下:

#include<stdio.h>

#include<stdlib.h>

 

#define M 10

 

void exchange(int *data, int p1, int p2)

{

    int tmp;

    tmp = data[p1];

    data[p1] = data[p2];

    data[p2] = tmp;

}

 

void compexch(int *data, int p1, int p2)

{

    if(data[p1]>data[p2])

        exchange(data, p1, p2);

}

 

void iSort(int *data, int begin, int end)

{

    int i,j,tmp;

   

    for(i=begin+1;i<=end;i++)

    {

        j = i-1;

        tmp = data[i];

        while(j>=begin && data[j]>tmp)

        {

            data[j+1] = data[j];

            j--;

        }

        data[j+1] = tmp;        

    }

    return;

}

 

//完成一次分区 

int partition(int *data, int begin, int end)

{

    int i,j,pivot; 

    i = begin;

    j = end;

    pivot = data[i];

    while(i<j)

    {

        while(i<j && data[j]>=pivot)

            j--;

        data[i] = data[j];

        while(i<j && data[i]<=pivot)

            i++;

        data[j] = data[i];

    }

    data[i] = pivot;

    return i;

}

void qSort(int *data, int begin, int end)

{

    int pi, povit; 

    int i, left, right;

    if(begin >= end)

        return;

    if(end-begin <= M) 

        iSort(data, begin, end);

    else

    {

        //1 选择中轴元素,将其设置到 begin+1 的位置上 

        exchange(data, begin+1, (begin+end)/2);

        compexch(data, begin, begin+1);

        compexch(data, begin, end);

        compexch(data, begin+1, end);

        //2 分区。此时 begin 和 end 两个位置已经不影响本次分区了

        pi = partition(data, begin+1, end-1);

        povit = data[pi];

        //3 构建等于中轴元素的中间堆

        left = pi;

        right = pi;

        for(i=begin;i<left;i++)

            if(data[i] == povit)

                exchange(data, i, --left);

        for(i=end;i>right;i--)

            if(data[i] == povit)

                exchange(data, i, ++right);

        //4 递归调用快速排序

        qSort(data, begin, left-1);

        qSort(data, right+1, end);        

    }

}

 

 

int main()

{

    int data[20];

    int n;

    int i,j;

    scanf("%d",&n);

    for(i=0;i<n;i++)

        scanf("%d",&data[i]);

    qSort(data, 0, n-1);

    for(i=0;i<n;i++)

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

    printf("\n");

    system("pause");

    return 0;

}


学习,永无止境!知道、会用一个算法,远远不够,掌握它,改进它,and 不断的改进它!


原创粉丝点击