快速排序算法学习整理

快速排序被认为是所有同数量级排序(O(n*logn))的排序方法中平均性能最好的,同时它也是C++sort,C语言qsort函数的底层实现(下面简述快排)。
个人理解快排是分治思想的排序,每次找一个中心点,将比其小的丢到它左边,比其大的丢到它右边(以排成递增序列为例),下次就对其左边和右边的两个子区间进行这样的操作。

思路:
每次选出一个中心点(最传统的选法是直接选区间第一个元素)先存到一个辅助空间上,再设两个指针,一个指向区间左端,一个指向区间右端:
对于右边的指针: 若指向的值比中心点小,就把它丢到左边上一个指向位置的空间中去(第一步的中心点就是从左指针位置选出,这样可以保证上一次位置的元素已成功已走,即该位置为空出状态);
对于左边的指针: 同理,若指向的值比中心点大,则丢到右边上一次指向的位置中去;
这样,快速排序的基本代码就可以写出来了。

以下以洛谷P1177为例分析传统快排及其改进。

代码:
1.传统快排:

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define maxn 100050

int my_array[maxn];

void quick_sort(int* my_array,int l,int r)
{
    if(l<r)
    {
    	int low=l;
        int high=r;
        int num=my_array[low];
        while(low<high)
        {
            while(low<high && my_array[high]>=num) high--;
            my_array[low]=my_array[high];

            while(low<high && my_array[low]<=num) low++;
            my_array[high]=my_array[low];
        }
        //当low==high时,循环结束
        my_array[low]=num;
        quick_sort(my_array,l,low-1);
        quick_sort(my_array,high+1,r);
    }
}

int main()
{
    int i,j;
    int n,m;
    scanf("%d",&n);
    for(i=1;i<=n;i++)
    {
        scanf("%d",&my_array[i]);
    }
    quick_sort(my_array,1,n);
    for(i=1;i<=n;i++)
    {
        if(i!=n)
        {
            printf("%d ",my_array[i]);
        }
        else
        {
            printf("%d\n",my_array[i]);
        }
    }
    return 0;
}

这样的快排由于每次中心点都是选取区间第一个数,则若初始序列已经基本有序了,则其便退化为起泡排序O(n^2)了。
以洛谷P1177为例:传统快排只能过前两个点,后面的三个点都被卡了时间。
在这里插入图片描述
改进的方法有"三者取中",
即每次比较array[low], array[(low+high)/2], array[high],取三者的关键值中值作为中心点,这样可大大改善最坏情况的性能。
将上面的传统快排中心点选取swap一下就好了。
代码:
2."三者取中"快排

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define maxn 100050

int my_array[maxn];

//取中值位置 
int find_mid(int* my_array,int pos1,int pos2,int pos3)
{
    int midpos;
    if((my_array[pos1]>=my_array[pos2] && my_array[pos1]<=my_array[pos3])||(my_array[pos1]<=my_array[pos2] && my_array[pos1]>=my_array[pos3]))
    {
        midpos=pos1;
    }
    else if((my_array[pos2]>=my_array[pos1] && my_array[pos2]<=my_array[pos3])||(my_array[pos2]<=my_array[pos1] && my_array[pos2]>=my_array[pos3]))
    {
        midpos=pos2;
    }
    else
    {
        midpos=pos3;
    }
    return midpos;
}

void quick_sort(int* my_array,int l,int r)
{
    if(l<r)
    {
        int low=l;
        int high=r;
        int mid=find_mid(my_array,low,(low+high)/2,high);
        swap(my_array[low],my_array[mid]);
        int num=my_array[low];
        while(low<high)
        {
            while(low<high && my_array[high]>=num) high--;
            my_array[low]=my_array[high];

            while(low<high && my_array[low]<=num) low++;
            my_array[high]=my_array[low];
        }
        //当low==high时,循环结束
        my_array[low]=num;
        quick_sort(my_array,l,low-1);
        quick_sort(my_array,high+1,r);
    }
}

int main()
{
    int i,j;
    int n,m;
    scanf("%d",&n);
    for(i=1;i<=n;i++)
    {
        scanf("%d",&my_array[i]);
    }
    quick_sort(my_array,1,n);
    for(i=1;i<=n;i++)
    {
        if(i!=n)
        {
            printf("%d ",my_array[i]);
        }
        else
        {
            printf("%d\n",my_array[i]);
        }
    }
    return 0;
}

这样改进后快排就效率高些了,过了4个点,最后一个点还是TLE了…
在这里插入图片描述然后学习到还有一种思路是选择随机数位置,即每次中心点的选择采用随机数确定。
代码如下:
3."随机数确定位置"快排

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define maxn 100005

int my_array[maxn];

void quick_sort(int* my_array,int l,int r)
{
    if(l<r)
    {
    	int low=l;
        int high=r;
        int rand_pos=rand()%(r-l+1)+l;//随机数确定位置; 
        swap(my_array[low],my_array[rand_pos]);
        int num=my_array[low];
        while(low<high)
        {
            while(low<high && my_array[high]>=num) high--;
            my_array[low]=my_array[high];

            while(low<high && my_array[low]<=num) low++;
            my_array[high]=my_array[low];
        }
        //当low==high时,循环结束
        my_array[low]=num;
        quick_sort(my_array,l,low-1);
        quick_sort(my_array,high+1,r);
    }
}

int main()
{
    int i,j;
    int n,m;
    scanf("%d",&n);
    for(i=1;i<=n;i++)
    {
        scanf("%d",&my_array[i]);
    }
    quick_sort(my_array,1,n);
    for(i=1;i<=n;i++)
    {
        if(i!=n)
        {
            printf("%d ",my_array[i]);
        }
        else
        {
            printf("%d\n",my_array[i]);
        }
    }
    return 0;
}

但是即使这样它还是过不了最后一个测试点,但是相比于三者取中测试的用时又少了一点(当然,因为是随机数,下次测也有可能多…)
在这里插入图片描述
原来最后一个测试点是全部元素值相同的数据序列,那这样的话所有的区间划分和递归都是无意义的…

无奈之下,函数swap改成手写swap,不用scanf输入数据,用手写版快读模板读入数据,加上O2优化,终于卡过了…

#include <cstdio>
#include <cstring>
#include <algorithm>
using namespace std;
#define maxn 100005

int my_array[maxn];

inline int read()//此方法只能读入整数;
{
    int sgn = 1; int cnt = 0; //sgn表示正负号,cnt表示读取的数字
    char ch = getchar();
    while (ch < '0' || ch > '9')//读取非数字的字符,保留负号,忽略其余无关符号
	{      
        if(ch == '-')
            sgn = -sgn;
        ch = getchar();
    }
    while ('0' <= ch && ch <= '9')
	{
        cnt = cnt*10 + (ch-'0');
        ch = getchar();
    }
    return  sgn*cnt;
}

int find_mid(int* my_array,int pos1,int pos2,int pos3)
{
    int midpos;
    if((my_array[pos1]>=my_array[pos2] && my_array[pos1]<=my_array[pos3])||(my_array[pos1]<=my_array[pos2] && my_array[pos1]>=my_array[pos3]))
    {
        midpos=pos1;
    }
    else if((my_array[pos2]>=my_array[pos1] && my_array[pos2]<=my_array[pos3])||(my_array[pos2]<=my_array[pos1] && my_array[pos2]>=my_array[pos3]))
    {
        midpos=pos2;
    }
    else
    {
        midpos=pos3;
    }
    return midpos;
}

void quick_sort(int* my_array,int l,int r)
{
    if(l<r)
    {
        int low=l;
        int high=r;
        int mid=find_mid(my_array,low,(low+high)/2,high);
        
        int t;
        //swap:
        if(mid!=low)
        {
            t=my_array[low];
            my_array[low]=my_array[mid];
            my_array[mid]=t;
        }
        
        int num=my_array[low];
        while(low<high)
        {
            while(low<high && my_array[high]>=num) high--;
            my_array[low]=my_array[high];

            while(low<high && my_array[low]<=num) low++;
            my_array[high]=my_array[low];
        }
        //当low==high时,循环结束
        my_array[low]=num;
        quick_sort(my_array,l,low-1);
        quick_sort(my_array,high+1,r);
    }
}

int main()
{
    int i,j;
    int n,m;
    scanf("%d",&n);
    for(i=1;i<=n;i++)
    {
        my_array[i]=read();
    }
    quick_sort(my_array,1,n);
    for(i=1;i<=n;i++)
    {
        if(i!=n)
        {
            printf("%d ",my_array[i]);
        }
        else
        {
            printf("%d\n",my_array[i]);
        }
    }
    return 0;
}

在这里插入图片描述所以说,C++sort,C语言qsort快排底层肯定实现没那么简单(上几百行代码…)
贴一小部分C中qsort和C++中sort实现代码(C语言用了指针做,C++用了迭代器做),有兴趣的大佬可以去编译器自行查看。

//C语言qsort:仅函数名部分
void __cdecl qsort(void *_Base,size_t _NumOfElements,size_t _SizeOfElements,int (__cdecl *_PtFuncCompare)(const void *,const void *));
#endif
//C++sort:
template<typename _RandomAccessIterator, typename _Compare>
    inline void
    sort(_RandomAccessIterator __first, _RandomAccessIterator __last,
	 _Compare __comp)
    {
      // concept requirements
      __glibcxx_function_requires(_Mutable_RandomAccessIteratorConcept<
	    _RandomAccessIterator>)
      __glibcxx_function_requires(_BinaryPredicateConcept<_Compare,
	    typename iterator_traits<_RandomAccessIterator>::value_type,
	    typename iterator_traits<_RandomAccessIterator>::value_type>)
      __glibcxx_requires_valid_range(__first, __last);

      std::__sort(__first, __last, __gnu_cxx::__ops::__iter_comp_iter(__comp));
    }
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值