快速排序的三种实现以及应用场景

来源:互联网 发布:远程桌面控制软件下载 编辑:程序博客网 时间:2024/06/06 00:57

好了,废话不多说,直接上干货。
1、快速排序的概念:
快速排序(Quicksort)是对冒泡排序的一种改进。
快速排序由C. A. R. Horno在1962年提出。它的基本思想是:通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快速排序,整个排序过程可以递归进行,以此达到整个数据变成有序序列。
2、快速排序的三种实现:

/*way 1:Horno法算法思想:1、找一个基准值key(一般三数取中法),本题中基准值为数组中最右的元素,再定义两个指针begin(指向首元素)和end(指向尾元素)2、begin从前往后走找比基准值key大的元素(找到后停下),end从后往前走找比基准值小的元素(找到后也停下),然后,交换array[begin]和array[end],依次循环操作。3、当begin与end相遇,将array[begin]或array[end]与基准值交换。*/int partion1(int *array,int left,int right){    int key = array[right];    int begin = left;    int end = right;    while (begin<end)    {        while (begin<end&&array[begin] <= key)//注意循环内部条件                                begin++;                            while (begin<end&&array[end] >= key)                    end--;          if (begin < end)        std::swap(array[begin], array[end]);    }    if (begin!=right)//防止自己和自己交换,即使自己和自己交换不会出错,但是会多执行以此交换函数,降低效率    std::swap(array[begin], array[right]);    return begin;}/*way2:挖坑法算法思想:1、定义begin和end分别指向数据的第一个元素和最后一个元素,基准值key为数组最后一个元素,array[end]元素的位置为一个坑2、begin从前往后走,找比key大的值,找到之后,将array[begin]赋值给array[end],填充end位置的坑,此时begin位置为一个坑3、end从后往前走,找比key小的值,找到之后,将array[end]赋值给array[begin],填充begin位置的坑,此时end位置为一个新的坑4、此类方法依次填坑,当begin和end相遇,则循环结束,将key的值填最后一个坑。*/int partion2(int *array, int left, int right){    int key = array[right];    int begin = left;    int end = right;    while (begin<end)    {        while (begin<end&&array[begin] <= key)//注意循环内部条件                            begin++;        if (begin < end)        {            array[end] = array[begin];            end--;        }           while (begin<end&&array[end] >= key)//注意循环内部条件                          end--;        if (begin<end)        {            array[begin] = array[end];            begin++;        }           }    if (begin != right)//防止自己和自己交换,即使自己和自己交换不会出错,但是会多执行以此交换函数,降低效率    std::swap(array[begin], array[end]);    array[begin] = key;    return begin;}/*way3:前后指针法算法思想:1、选择一个基准值key,定义两个指针pPre和pPcur(pPre指向pPcur的前一个位置),pPre和pPcur同时走,当pPcur标记的元素比key大时,只有pPcur继续向前走(此时pPre停下来),当pPcur走到标记的元素比pPre小时,pPcur停下,此时交换array[pPcur]和array[pPre+1],然后,pPre往前走一步,pPcur继续往前走。2、当pCur走出界了,则将pPre+1位置的值与key交换。*/int partion3(int *array, int left, int right){    int pPcur = left;    int pPre = left-1;    int key = array[right];    while (pPcur<right)    {        if (array[pPcur ]< key)        {            swap(array[pPre + 1], array[pPcur]);            pPre++;        }         pPcur++;        }    swap(array[right], array[++pPre]);    return pPre;}void QuickSort(int *array,int left,int right ){    if (left < right)    {        int goal = partion2(array, left, right);        QuickSort(array, left, goal-1);        QuickSort(array, goal+1, right);    }}3、快速排序的优化://三数取中法://快速排序需要找基准值,为了避免找的基准值是最大的数,或者是最小的数,(基准值最大或者最小,效率最差)//快速排序可用三数取中法优化,以下是三数取中法代码:int ThreeToMid(int a, int b, int c)//1  3  5{    if (a>b)    {        if (b > c)            return b;        else        {            if (a < c)                return a;            else                return c;        }    }    else    {        if (b < c)            return b;        else        {            if (a>c)                return a;            else                return c;        }    }}4、性能分析:(1)若选择的基准值是数据中最大(小)的数,但是要排成降序(升序),则性能最低,为了改变这种特殊情况,故选择基准值的时候采用了三数取中法。(2)将升序序列(降序)排成降序(升序)序列,效率也是最低的。(3)若每以基准值划分过程产生的区间大小都为n/2,则快速排序法运行就快得多了。综上所述,快速排序的最好情况下的时间复杂度为O(N*logN),最坏情况下的时间复杂度为O(N^2),平均的时间复杂度为O(N*logN)。空间复杂度为O(1),当然了,快速排序是属于不稳定排序。综合以上情况的对比,快速排序适用于数据杂乱无章的场景,而且越乱,快速排序的效率越体现的淋漓尽致。5、应用场景:                ------------求数组中第k小的数------------加入数组arr中数据是:4,0,1,0,2,3,那么第三小的元素是1,问怎么样快速的求出这个第k小的数。解法一:  利用STL中快速排序把数组进行排序,然后数组下标是k-1的就是我们要求的第k小的元素了,这种情况的时间复杂度接近于O(n*logn)。但是回头想一想这个算法是把数组进行了全排序,而我们只是要找到数组中第k小的数,这显然是“杀猪用了牛刀”。当然了时间复杂度较高也是正常的。解法二:  想一想快速排序的思想:将数组中某一个元素m作为划分依据(我们假设为第一个元素,即m = arr[0]),遍历一遍数组,使得数组的格局变成这样的三个部分:(1)m前面的元素 (2)m  (3)m后面的元素。其中m前面的元素小于m,m后面的元素大于m,这样找第k小的数正好可以借鉴这个思想,即:a、若m前面的元素个数大于k,则第k小的数一定在m前面的元素中,这时我们只需要继续在m前面的元素中搜索第k小的数;b、若m前面的元素个数小于k,则第k小的数一定在m后面的元素中,这是我们只需要继续在m后面的元素中搜索第k-s小的数,其中s是m前面的元素个数。代码如下:#include <iostream.h>#define MAX_SIZE 100int Biger[MAX_SIZE];int Smaller[MAX_SIZE];int Select_kth_Small(int arr[],int n,int k){    if(n == 1)        return arr[0];    int b = 0,s = 0,t = arr[0],temp_n,temp_k;    int temp[MAX_SIZE];    for(int i = 1 ; i < n ; i++)//遍历集合    {        if(arr[i] > t)            Biger[b++] = arr[i]; //如果当前元素比t大,就将当前元素加入Biger[]        else            Smaller[s++] = arr[i];//反之就加入到Smaller,这里没有考虑set[0]    }    if(b == 0)    {        Biger[b++] = t;//if...else主要是为了防止t大于或小于其他所有元素的情况    }    else    {        Smaller[s++] = t;    }    //如果Smaller集合中的元素个数大于K,说明第K小的元素必在其中    //否则一定在Biger中,且应该是Biger集合中第k-r小的元素    //更新相应的变量    if(s >= k)    {        temp_n = s;        temp_k = k;        for(i=0;i<temp_n;i++)        {                    temp[i] = Smaller[i];        }    }            else    {        temp_n = b;        temp_k = k-s;        for(i=0;i<temp_n;i++)        {            temp[i]=Biger[i];            }    }    return Select_kth_Small(temp,temp_n,temp_k);}int main(){    int arr[]={4,0,1,0,2,3};    int ans = Select_kth_Small(arr,6,3);    cout<<"在数组arr[]={4,0,1,0,2,3}中,第3小的数是:"<<ans<<endl;    return 0;}
原创粉丝点击