快速排序

快速排序(优化版)

本题来自洛谷(快速排序)
原题地址(https://www.luogu.org/problemnew/show/P1177)
直接进入正题。
快速排序的大致思路:先找到一个基数(随便找,但是有一般有一个习惯),基数用于与所有数比较,使左边的数比基数小,右边的数比基数大,把左边和右边的数看作新的数列,再进行新的排序。当左右的数列只有一个数结束。(这个算法的的时间复杂度是不确定的)
图片来自《啊哈!算法》
原数列为6,1,2,7,9,3,4,5,10,8。
在这里插入图片描述
第一步:
一般把最左边的数作为基数(temp),把最左边数设为i,把最右边的数设为j,现在从最右边(j)开始找,因为你需要一个结束条件(为i,j同时指到一个数),最后你要把你选择的基数(6)和中间的分艰线的值进行交换,所以,你要交换的数必须比基数(6)小,上一次交换后i指的数是比6小的数(4),当j走到与i相同时,不再走,这时i,j都指的是比基数(6)小的数这样进行交换才能保证交换后的数列变成我们想要的样子。选择的数都为比6小的数,你想:如果要把基数(6)右边的数都大于基数(6),基数(6)左边的数小于基数(6),你应该怎么办?
在这里插入图片描述
显然当你(j)往左走时,遇到比6小的数停下,等着他的兄弟(i)找到一个比6大的数,他们的数交换;当遇到两个数相同时(i=j),再把i位和最左位的数进行交换就行了。
在这里插入图片描述
第二步:
再把基数(6)左的数组右面的数组分别进行排序,直到左边界等于或者大于右边界。

本以为这道题就做完了,但是5个样例只通过了1个,其余4个为超时。这时就要对代码进行优化。
在这里插入图片描述
第一步优化(基数随机化):
原本我们找的数为最左边的数,如果现在我们的数列为从大到小的顺序,你就要一个一个的进行排序,时间复杂度退化为O(n^2),还是会超时,这时我们就把基数进行随机化,基数取数列中任意一个,这样可以大大减少时间。
第二步优化(小序列简单排序):
快速排序虽然快速,但是当序列小的时候要进行非常多次递归,这样时间会增加很多,而一些简单的排序(冒泡排序,选择排序)在小序列时更加的快速。(如上图)
第三步优化(除去相等的数):
当进行一次排序以后,如果与基数相邻的数都为基数相同,那么就不必再把相同的数列入下一个序列进行排列了,比如:基数为中间的8时,两旁的8就没有必要再进行排序了。
综合以上方法写出了代码,也是能全部AC的。

#include<bits/stdc++.h>
using namespace std;
long int  f[100005],temp,i,j;
void swap(long int &a,long int &b)
{
 long int n=a;
 a=b;
 b=n;  
}
void asort(int left,int right)
{
 for(int i=left;i<=right;i++)
 {
   for(int j=i;j<=right;j++)
   {
    if(f[j]<f[i])
    swap(f[i],f[j]);
   }
 }
 return;
}
void qsort(int left,int right){
 if(left>=right) return;
 if(right-left<=10)
  {
 asort(left,right);
  return;
 }
 temp=rand()% (right - left + 1) + left;swap(f[temp],f[left]);
 i=left;
    temp=f[left];
    j=right;
    while(i!=j)
    {
    while(f[j]>=temp&&i<j)
    j--;
    while(f[i]<=temp&&i<j)
    i++;
    if(i<j)
    {
 swap(f[i],f[j]);
    } 
    } 
    {
     f[left]=f[i];
     f[i]=temp;
    }
    while(f[i]==temp&&i>=left)
     i--;
  qsort(left,i);
  while(f[j]==temp&&j<=right)
     j++;
  qsort(j,right);
}
int main()
{
   int n;
   scanf("%d",&n);
   for (int i = 0; i < n; i++)
        scanf("%d", &f[i]);
    qsort(0,n-1);
    for (int i = 0; i < n; i++)
        printf("%d ", f[i]);
   return 0;
}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值