题目:
给定一个长度为 n的整数数列,以及一个整数 k,请用快速选择算法求出数列从小到大排序后的第 k 个数。
输入格式
第一行包含两个整数 n 和 k。
第二行包含 n 个整数(所有整数均在 1∼1091∼109 范围内),表示整数数列。
输出格式
输出一个整数,表示数列的第 k 小数。
数据范围
1≤n≤100000
1≤k≤n
输入样例:
5 3 2 4 1 5 3
输出样例:
3
思路分析 :
一、
1、看到本题首先想到的是排序,先将这n个数进行排序,然后对排序结果的第k个数进行输出即可。
2、接下来就是要想我们要用什么排序。为了保证时间复杂度,对于冒泡、插入等排序这里就不考虑了。我们需要斟酒的是归并和快排这两种排序方式的选择,因为标准情况下两者的时间复杂度相同。都为O(nlogn)。当然,快排的时间复杂度不是很稳定,只有最理想的情况才是O(nlogn),最坏的情况是O(n^2),不过它的数学期望就是O(nlogn)。而归并排序的时间复杂度就是稳定的O(nlogn)。
二、
1、延续上面的时间复杂度分析,如果没有特殊情况我们应该选择归并排序。但是本题的要求并不是完全的排序而是只要选出从小到大排序后的第k个数就可以。
2、让我们把目光转到快速排序。快排是首先对整体进行排序:选择一个x,然后整体上把左边数变为小于x,右边大于x。后续:再分别同过递归对左右两边进行排序。看到这里实际上我们就知道了,既然快排整体上已经有序的,那么我们就可以通过对x左边数据量和k的大小比较 ,确定k是在左边子区间还是右边子区间。如此就只要对一边继续递归便可以。
下面我通过手写图来举个例子:
3、对快排这样改进后用于选择的算法称为:快速选择算法。其时间复杂度为O(n)。图解如下:
本人字真的很丑,勿喷,呜呜呜
代码如下:
#include<iostream> using namespace std; void quick_sort(int a[],int l,int r,int k){ if(l==r)return; int i=l-1; int j=r+1; int x=a[(l+r)>>1]; while(i<j){ do i++;while(a[i]<x); do j--;while(a[j]>x); if(i<j) swap(a[i],a[j]); } //到目前为止本算法和快速排序算法还是相同的,就是将一个大的区间分为两个整体大小确定的子区间 int sl=j-l+1;//左子区间的长度 if(sl>k) quick_sort(a,l,j,k); else quick_sort(a,j+1,r,k-sl);//k-sl是表示整体第k个数在右子区间中的第k-sl的位置 //这三行代码是对两个子区间进行选择,因为第k个数只会在其中一个区间,所以只需要对其中一个进行递归排序 } int main(){ int num,a=0; int b[100001]={0}; cin>>num>>a; for(int i=0;i<num;i++){ cin>>b[i]; } quick_sort(b,0,num-1,a-1); cout<<b[a-1]<<endl; }