求第k小的数(魔改为前k大的数)

题面如下
题面

先来了解一下什么是快速排序。
快速排序
是一种基于分治思想的排序策略,大体步骤分为三步
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右边,而实现这个操作就使用了一个很巧妙的方法
取l和r的中间地址(向下取整)
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排序,最后再乘回来,就实现了从大到小排序。

Guess you like

Origin blog.csdn.net/qq_43070117/article/details/111260669
k
Recommended