快速排序和随机快速排序

一、问题描述

实现对数组的普通快速排序与随机快速排序
(1)实现上述两个算法
(2)统计算法的运行时间
(3)分析性能差异,作出总结

二、算法原理:

1、快速排序

快速排序是对冒泡排序的一种改进。它的基本思想是:选取一个基准元素,通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比基准元素小,另外一部分的所有数据都要比基准元素大,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
设要排序的数组是A[0]……A[N-1],首先选取一个数据(普通快速排序选择的是第一或者最后一个元素, 随机快速排序是随机选择一个元素)作为关键数据,然后将所有比它小的数都放到它前面,所有比它大的数都放到它后面,这个过程称为一趟快速排序。
一趟快速排序的算法是:
1)设置两个变量i、j,排序开始的时候:i=0,j=N-1;
2)以第一个数组元素作为关键数据,赋值给key,即key=A[0];
3)从j开始向前搜索,即由后开始向前搜索(j–),找到第一个小于key的值A[j],将A[j]赋给A[i];
4)从i开始向后搜索,即由前开始向后搜索(i++),找到第一个大于key的A[i],将A[i]赋给A[j];
5)重复第3、4步,直到i=j; (3,4步中,没找到符合条件的值,即3中A[j]不小于key,4中A[i]不大于key的时候改变j、i的值,使得j=j-1,i=i+1,直至找到为止。找到符合条件的值,进行交换的时候i, j指针位置不变。另外,i==j这一过程一定正好是i+或j-完成的时候,此时令循环结束)。
本算法实现用到随机的500个数据进行试验,随机产生的数据如下图:
这里写图片描述

经过快速排序后结果如下:

这里写图片描述

快速排序代码如下:

#include <iostream>
#include<time.h>
#include<stdlib.h>

#define NUMBER 500//产生待排随机数的个数
using namespace std;

void QuickSort(int *A,int l,int r)
{
    int temp;
    int i=l,j=r;
    if(l<r)
    {
        temp=A[l];
        while(i!=j)
        {
            while(j>i&&A[j]>temp) --j;//从右边扫描找到一个小于temp元素

            if(i<j)
            {
                A[i]=A[j];
                ++i;
            }
            while(i<j&&A[i]<temp) ++i;//从左边扫描,找到一个大于temp元素

            if(i<j)
            {
                A[j]=A[i];
                --j;
            }
        }
        A[i]=temp;//temp放在最终位置
        QuickSort(A,l,i-1);//对temp左边元素进行扫描
        QuickSort(A,i+1,r);//对temp右边元素进行扫描
    }
}

int main(void)
{
    srand((unsigned)time(NULL));
    clock_t begin_time,end_time;
    int *p;

    p=new int[NUMBER];

    for(int k=0;k<NUMBER;k++)
    {
        p[k]=rand()%NUMBER;
        cout << p[k]<<" ";
    }
    cout<<endl<<endl;

    begin_time=clock();
    QuickSort(p,0,NUMBER-1);
    end_time=clock();

    for(int i=0;i<NUMBER;i++)
    {
        cout<<p[i]<<" ";
    }

    cout<<endl<<endl<<end_time-begin_time<<" ms"<<endl;
    return 0;
}

2、随机快速排序

快速排序的最坏情况基于每次划分对主元的选择。基本的快速排序选取第一个或者最后一个元素作为主元。这样在数组已经有序的情况下,每次划分将得到最坏的结果。一种比较常见的优化方法是随机化算法,即随机选取一个元素作为主元。这种情况下虽然最坏情况仍然是O(n^2),但最坏情况不再依赖于输入数据,而是由于随机函数取值不佳。实际上,随机化快速排序得到理论最坏情况的可能性仅为1/(2^n)。所以随机化快速排序可以对于绝大多数输入数据达到O(nlogn)的期望时间复杂度。
贴上随机快速排序随机产生的数字,然后再进行排序,图如下,然后在对代码进行分析:
这里写图片描述

随机快速排序的结果如下:
这里写图片描述

随机快速排序和快速排序的差别就是在选取主元(枢轴)的方式不一样而已,快速排序一般选取第一个或者最后一个,随机快速排序则是随机选取的主元。为了方便,我下面的代码随机选取主元后,把主元放到最后一个位置,然后再进行排序,代码如下:

#include <iostream>
#include<stdlib.h>
#include<time.h>

#define NUMBER 500
using namespace std;

//两数交换
void exchange(int &a,int &b)
{
    int temp;
    temp=a;
    a=b;
    b=temp;
}
//p表示将数组A排序的起始下标,r是结束下标
int random_patition(int *A,int p,int r)
{
    int temp;
    int i=p-1;
    //产生随机数组下标
    int k= p + rand()%(r -p +1);
    //仍然将随机的枢轴交换到最后
    exchange(A[r],A[k]);
    temp=A[r];

    for(int j=p;j<=r-1;j++)
    {
        if(A[j]<=temp)
        {
            i=i+1;
            exchange(A[i],A[j]);
        }
    }
    //最后主元交换
    exchange(A[i+1],A[r]);
    return i+1;
}

//递归调用
void QuickSort(int *A,int p,int q)
{
    if(p<q){
        int r = random_patition(A, p, q);
        QuickSort(A, p, r-1);
        QuickSort(A, r+1, q);
    }

}

int main(void)
{
    srand((unsigned)time(NULL));
    clock_t begin_time,end_time;
    int *p;
    p=new int[NUMBER];

    for(int k=0;k<NUMBER;k++)
    {
        p[k]=rand()%NUMBER;
        cout << p[k]<<" ";
    }
    cout<<endl<<endl;

    begin_time=clock();
    QuickSort(p,0,NUMBER-1);
    end_time=clock();

    for(int i=0;i<NUMBER;i++)
    {
        cout<<p[i]<<" ";
    }

    cout<<endl<<endl<<end_time-begin_time<<" ms"<<endl;

    return 0;
}

我的上述代码排序方式稍微和快速排序有点差别,但是都是就地排序。

三、结果分析

快速排序的平均时间复杂度为O(nlogn),最坏时间时间可达到O(n^2),最坏情况是当要排序的数列基本有序的时候。根据快速排序的工作原理我们知道,选取第一个或者最后一个元素作为主元,快速排序依次将数列分成两个数列,然后递归将分开的数列进行排序。当把数列平均分成两个等长的数列时,效率是最高的,如果相差太大,效率就会降低。
我们通过使用随机化的快速排序随机的从数列中选取一个元素与第一个,或者最后一个元素交换,这样就会使得数列有序的概率降低。所以随机快速排序平均速度是比快速排序要快的。

随机化快速排序的平均时间复杂度为O(nlogn),最坏时间时间也会达到O(n^2),这种机率已经降得很小。

猜你喜欢

转载自blog.csdn.net/qq_22771739/article/details/83589073