题面如下
题面
先来了解一下什么是快速排序。
快速排序
是一种基于分治思想的排序策略,大体步骤分为三步
1.确定分界点
2.调整区间 i,j指针
3.递归处理左右两段
下面给出一组数模拟一遍快速排序的过程
地址 1 2 3 4 5 6 7
数据 1 5 7 4 2 3 8
首先,要确定左右边界l,r分别为1和7。定义两个指针 i和j分别指向1和7,及i=1,j=7。
艹这也太难写了,我还是画图吧
快排每一次要做的工作是把所有比mid小的数挪到mid左边,把比mid大的数挪到mid右边,而实现这个操作就使用了一个很巧妙的方法
mid取l和r的中间地址(向下取整)
分别将i和j指向的数与mid所指向的数作比较,i指向的数如果比mid指向的数小,那么i++,挪到下一个地址继续比较,知道遇到一个i指向的数比mid指向的数大,那么先打住。j同理,遇到第一个比mid指向的数小的,也打住。
现在,图变成了这个样子。
我们只需要把这两指针指向的数巧妙的交换,就实现了快排的每一次工作。
重复这样的工作当i>=j。快速排序的第一步操作就完成了。
及mid左边的数都比mid小,mid右边的数都比mid大。
你可能会问,快排不是要用分治的思想吗,分治呢?
接下来就是分治
只要我们继续将已经分成两份的数据分割,进行上述操作,那么就保证了每一个子数据内部是有序的,而且符合大部队的顺序,因为每次分割的区间内的数据,比如[l,mid]内的所有数都是大于[mid+1,r]的,所以可以保证最后的数据是整体有序的。
具体代码通过递归来实现。
我们继续用图演示。先演示[l,mid]
我们将r更新为上一级的mid,mid更新为l和当前r的中间地址(向下取整)继续刚才的操作。操作完的数据变成这样。
可以发现现在这一段数据已经有序了,但是人家电脑不知道。所以还要继续分,直到分到不能再分,及l=r时,跳出递归,所以l=r是分治递归的出口。
(对于第一次拆分的[mid+1,r]这个,也进行相同的操作)
#include<iostream>
#include<cstdio>
//1.È·¶¨·Ö½çµã
//2.µ÷ÕûÇø¼ä i,jÖ¸Õë
//3.µÝ¹é´¦Àí×óÓÒÁ½¶Î
using namespace std;
int p[100000010];
int n,m;
void quicksort(int p[],int l,int r)
{
if(l>=r) return;
int i=l-1,j=r+1;
int x=p[(r+l)/2];
while(i<j)
{
do i++; while(p[i]<x);
do j--; while(p[j]>x);
if(i<j) swap(p[i],p[j]);
}
quicksort(p,l,j);
quicksort(p,j+1,r);
}
int main()
{
cin>>n>>m;
for(int i=1;i<=n;i++) scanf("%d",&p[i]);
quicksort(p,1,n);
printf("%d ",p[m+1]);
return 0;
}
理解了快排,求前k名学生也就变的很简单了。
快排需要的是整组数据有序,而求前k名,我们只需要将第k名最后的最小分组内的地址确定即可,不需要处理它后面的数或前面的数,因为它们是大体有序的。比如说
处理到一半数据1 3 2 4 7 5 8(这是以4为基准排序一次的结果及4之前都是比4小的数)
我们要知道第4小的数,不需要进一步处理这组数据,应为4之前的数都比4小,而4之后也没有比4小的数,所以4的地址就是答案。所以我们只要每次确认k这个地址在[l,mid]还是[mid+1,r],只去处理k所在的区间即可。
#include<iostream>
#include<cstring>
using namespace std;
struct node//定义结构体
{
int score;//分数
int stu_num;//学号
string name;//姓名
} p[10000];
void quick_sort(node p[],int l,int r,int k)
{
int x=p[(l+r)/2].score;
int i=l-1,j=r+1;
while(i<j)
{
do i++; while(p[i].score<x);
do j--; while(p[j].score>x);
if(i<j)
{
swap(p[i],p[j]);
//swap(p[i].stu_num,p[j].stu_num);
//swap(p[i].name,p[j].name);
}
}
if(k<j) quick_sort(p,l,j,k);//寻找第k位是否在(l,j) 是的话继续分
else if(k>j) quick_sort(p,j+1,r,k);//寻找第k位是否在(j+1,r)
else return;
}
int main()
{
int n,k;
cout<<"请依次输入学生人数n,前k名学生"<<endl;
cin>>n>>k;
cout<<"请依次输入" <<n<<"名学生的学号,姓名,分数"<<endl;
for(int i=1;i<=n;i++)
{
cin>>p[i].stu_num>>p[i].name>>p[i].score;
p[i].score*=-1;
}
quick_sort(p,1,n,k);
cout<<"前k名学生为"<<endl;
for(int i=1;i<=k;i++)
cout<<p[i].stu_num<<' '<<p[i].name<<' '<<p[i].score*-1<<endl;
//printf("%d ",quicksort(p,1,n,k));
return 0;
}
只要聪明地 将数据乘以-1排序,最后再乘回来,就实现了从大到小排序。