总算通过了,快排。虽然可以用简单的sort解决(以前我就是),但是自己写呢?!有几个数据一直过不去,难道书上错了?
显然是需要在基本快排上进行优化!!!
建议大家可以去试下 快排测试,卡了我好久。。。
一、书上的普通快排
我们看看普通的快排(分析网上多的是,随便找一篇吧),以最后一个为基准,显然效率不是很稳定,最坏情况会退化到O(n^2)
用这种快排能只通过2个数据 我提交上去时间大该在 9640ms ........
/*快排一,显然期望时间是好的,但是数据会刁难*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define Max 100050
void swap(int a[],int i,int j)
{
int num=a[i];
a[i]=a[j];
a[j]=num;
}
void q_sort1(int a[],int l,int r)
{
if(l>=r)
return;
int x=a[r];
int i=l,min=l-1;
while(i<r)
{
if(a[i]<=x)
{
swap(a,i,min+1);
min++; //比它小的数的个数
}
i++;
}
min=min+1; //后面那位才是大于它的数
swap(a,min,r); //交换
q_sort1(a,l,min-1);
q_sort1(a,min+1,r);
}
void show(int a[],int n)
{
for(int i=0;i<n;i++)
{
printf("%d",a[i]);
if(i!=n-1)
printf(" ");
else
printf("\n");
}
}
int a[Max];
int main()
{
int a[]={1,0,5,9,8,7,6,3,0,4};
int n=10;
// int n;
// scanf("%d",&n);
// for(int i=0;i<n;i++)
// scanf("%d",&a[i]);
q_sort1(a,0,n-1);
show(a,n);
return 0;
}
二、随机化快排
我们很自然的想到了让快排随机,不错,情况比上面好点,能通过3组数据,但是数据还是刁难你。
其实只要你运气好,啥数据不能过,是吧。(手动狗头)
我提交这个数据,情况变好一点了,过了三组数据,但是时间是 6652ms......
/*添加随机数后的快排*/
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define Max 100050
void swap(int a[],int i,int j)
{
int num=a[i];
a[i]=a[j];
a[j]=num;
}
void q_sort1(int a[],int l,int r)
{
if(l>=r)
return;
/*我们添加随机数 */
int num = rand()%(r-l+1)+l;
int x=a[num];
a[num]=a[r];
a[r]=x;
/* -------- */
x=a[r];
int i=l,min=l-1;
while(i<r)
{
if(a[i]<=x)
{
swap(a,i,min+1);
min++; //比它小的数的个数
}
i++;
}
min=min+1; //后面那位才是大于它的数
swap(a,min,r); //交换
q_sort1(a,l,min-1);
q_sort1(a,min+1,r);
}
void show(int a[],int n) //输出
{
for(int i=0;i<n;i++)
{
printf("%d",a[i]);
if(i!=n-1)
printf(" ");
else
printf("\n");
}
}
int a[Max];
int main()
{
// int a[]={1,0,5,9,8,7,6,3,0,4};
// int n=10;
int n;
scanf("%d",&n);
for(int i=0;i<n;i++)
scanf("%d",&a[i]);
q_sort1(a,0,n-1);
show(a,n);
return 0;
}
三、快排优化折磨区域
(一)、两边同时查找最大元素,最小元素,然后交换,减少循环的次数。
原本的快排每次只找一个数,然后判断是否交换,我们可以先找到一个大于基准的数,然后找到一个小于基准的数,然后交换他们两个,我也感觉快些(我没去证明.......)。
方法是不错啊,但是还是只能通过三组数据,时间是: 6526ms.......(快了一点呢,┭┮﹏┭┮)
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define Max 100050
void swap(int a[],int i,int j)
{
int num=a[i];
a[i]=a[j];
a[j]=num;
}
void q_sort1(int a[],int l,int r)
{
if(l>=r)
return;
/*我们添加随机数 */
int num = rand()%(r-l+1)+l;
int x=a[num]; //基准
a[num]=a[r];
a[r]=x;
/* -------- */
int i=l,j=r;
/*注意是先把最后一个覆盖,然后再交换,用覆盖来代替*/
while(i<j)
{
while(i<j&&a[i]<=x)
{
i++;
}
a[j]=a[i];//第一个大于基准的数会覆盖最后一个,就是我们的基准
while(i<j&&a[j]>x)
{
j--;
}
a[i]=a[j];
// 覆盖后 总是有个位置是多余的,因为我们把基准位置拿出来用了
}
a[i]=x; //基准归位 ,第i个位置就是我们基准的位置
q_sort1(a,l,i-1);
q_sort1(a,i+1,r);
}
void show(int a[],int n)
{
for(int i=0;i<n;i++)
{
printf("%d",a[i]);
if(i!=n-1)
printf(" ");
else
printf("\n");
}
}
int a[Max];
int main()
{
// int a[]={1,0,5,9,8,7,6,3,0,4};
// int n=10;
int n;
scanf("%d",&n);
for(int i=0;i<n;i++)
scanf("%d",&a[i]);
q_sort1(a,0,n-1);
show(a,n);
return 0;
}
(二)、下面的我就只写思路了,因为这些方法还是都没过。(尴尬)
1、当数据较小的时候(网上看的大概15个数),我们可以用插入排序代替快排,效率还会快些。
我们来看时间 6517ms(我只加了判断数据大小和插入排序),貌似又好了一点。
2、我们为什么要依靠随机选基准呢,所以用三分法优化快排,在需要排序的数据选三个数 (第一个数,中间一个数,最后一个数),然后选则三个数的中间值作为基准。(不怕告诉你,倒数第二组数据是从100000开始,逆序的,中间还有若干个重复的50000,貌似对这组数据很棒呢),结果是 6540ms (更尴尬了.....),而且倒数第二组数据还没过。
3、难道是重复元素多了,导致不能ac, 然后我自己想了个算法,如果元素和基准相同都放到最后面,然后记录有多少个相同的元素,最后一起换过去。(显然我这算法结果不乐观,好像也是6600多ms),但是后面我会发现,思路没错,算法太low 了。......
应该对这倒数第二组数据友好啊??,后来我感觉可能是在选中间值的时候,判断次数太多了,反而造成时间过长?(需要分析,证明,上课无聊 再去偷偷证明,哈哈)还是的确优化了,但是还是不能通过,因为我们只能看到最后超时的结果,而且下载不了最后一组数据,我的电脑跑倒数第二组数据会栈溢出......。
四、AC区
(一)、并归和堆排序
我测试了下并归排序和堆排序,结果 都过了,有点惊讶,我记得快排是比他们两个快的,所以问题再在,这题就是要测试你的快排,找的都是针对快排的数据。时间 分别事 127ms 和137ms 它摧毁的是一个好学者对快排的信心(可以用sort一遍过,才发现那些写STL多厉害,一样的快排到我手里就这么不争气,好吧是我不争气......)
(二)、三路划分的快排
其实经过折磨区,我知道一些情况会对排序影响,当元素是逆序的情况、重复元素多的情况,Mcllroy[248]说明了如果设计杀手级对手,让快排的几乎所有运算都是Θ(n^2)(魔鬼...)
这次可以AC,其实思路就是把大于基准,等于基准,小于基准的分块放,然后就是简洁,不做多余的事(这也就是为什么我前面想到类似的方法,却没AC.......) 这次的时间:152ms 总算是 AC了,嘿嘿。
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#define Max 100010
#define max(a,b) a>b?a:b;
#define min(a,b) a>b?b:a;
#define exchange(one,two) {int c=one;one=two;two=c;};
/*
void exchange(int &one,int &two)
{int c=one;one=two;two=c;} //会报错...
*/
void insert_sort(int a[],int low,int end)//开始位置到结束位置
{
for(int i=low+1;i<=end;i++)
{
int num=a[i];
int j=i;
while(j>low&&num<=a[j-1]) //从后面往前面换
{
exchange(a[j],a[j-1]);
j--;
}
}
}
int partition(int a[],int p,int r)
{
int x;
/*one*/
// int num=(rand()%(r-p+1)+p); //随机化
// exchange(a[num],a[r]);
// x=a[r];//选择基准
// int i=p;
// for(int j=p;j<r;j++) //最后一个不需要换
// {
// if(a[j]<=x)
// {
// exchange(a[j],a[i]);
// i++;
// }
// }
// exchange(a[i],a[r]);
// return i;
/*two*/
// int num=(rand()%(r-p+1)+p); //随机化
// exchange(a[num],a[r]);
// x=a[r];//选择基准
// int i=p,j=r;//边界范围
// while(i<j)
// {
// while(i<j&&a[i]<=x) //从前面开始找找,大于基准的数
// {
// i++;
// }
// exchange(a[i],a[j]);
// while(i<j&&a[j]>x) //从后面
// {
// j--;
// }
// exchange(a[i],a[j]);
// }
// a[i]=x;
// return i;
}
void quick_sort(int a[],int begin,int end)
{
if(begin<end)
{
if(end-begin<=15)
{
insert_sort(a,begin,end);
return;
}
int q=partition(a,begin,end);
quick_sort(a,begin,q-1);
quick_sort(a,q+1,end);
}
}
void quick_sort_3(int a[],int p,int r)
{
// if(r-p<=15)
// {
// insert_sort(a,p,r);
// return;
// }
if(p>=r)
return ;
int x;
int num=(rand()%(r-p+1)+p); //随机化
exchange(a[num],a[r]);
x=a[r];//选择基准
int left=p-1,right=r+1,i=r-1; //最后一个是基准
while(i>left)
{
if(a[i]<x) //小于的数放前面
{
exchange(a[left+1],a[i]);
left++;
}
else if(a[i]>x) //大于的数放后面
{
exchange(a[right-1],a[i]);
i--;
right--;
}
else //中空区市等于基准的数
{
i--;
}
}
quick_sort_3(a,p,left);
quick_sort_3(a,right,r);
}
void show(int a[],int n)
{
for(int i=0;i<n;i++)
{
printf("%d",a[i]);
if(i!=n-1)
printf(" ");
else
printf("\n");
}
}
int a[Max];
int main()
{
int n;
scanf("%d",&n);
for(int i=0;i<n;i++)
{
scanf("%d",&a[i]);
}
quick_sort_3(a,0,n-1);
// int b[]={1,0,9,8,5,6,7,4,3,2};
// n=10;
// show(b,n);
// quick_sort_3(b,0,n-1);
// show(b,n);
show(a,n);
return 0;
}
总算是把快速排序解决了,算法路子走的有点难,还有半年加油!!!