洛谷 P1923【深基9.例4求第 k 小的数
【深基9.例4】求第 k 小的数
题目链接:【深基9.例4】第k小的数
题目描述
输入 n n n( 1 ≤ n < 5000000 1 \le n < 5000000 1≤n<5000000 且 n n n 为奇数)个数字 a i a_i ai( 1 ≤ a i < 10 9 1 \le a_i < {10}^9 1≤ai<109),输出这些数字的第 k k k 小的数。最小的数是第 0 0 0 小。
请尽量不要使用 nth_element
来写本题,因为本题的重点在于练习分治算法。
样例输入
5 1
4 3 2 1 5
样例输出
2
思路
回顾快排:
对于快速排序,每一轮次的交换后都会使基准数到达它最终应该在的位置。
我们可以通过基准数最终在的位置来选择下一轮交换在哪半子数列中进行(ps:另一半的子数列就不用在排序了) 。
①如果一轮交换后基准数位置大于k,就从左半数列中运用快排的下一轮交换(因为第k小的数应该在基准点的左边)。然后得到下一轮基准数最终的位置,看是否是k,是则返回基准点的值。
②如果<k,就从右半子数列中运用快排…
重复①②直到得到最终的位置是k的基准数。
代码实现(java)
注意点:
-
使用Scanner读取数据会超时!
-
如果使用BufferedReader用以下方式读取输入的数据会导致MLE!因为多了个line1字符串数组的开销。
BufferedReader reader = new BufferedReader(new InputStreamReader(System.in));
String[] line1=reader.readLine().split(" ");
n=Integer.parseInt(line1[0]);
k=Integer.parseInt(line1[1]);
line1=reader.readLine().split(" ");
for (int i = 0; i <n; i++) {
arr[i]=Integer.parseInt(line1[i]) ;
}
可以使用StreamTokenizer进行快读。
非递归写法代码
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;
public class Main {
static int[] arr;
static int n,k;
public static void main(String[] args)throws Exception {
StreamTokenizer st = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
st.nextToken();
n=(int)st.nval;
st.nextToken();
k=(int)st.nval;
arr=new int[n+1];
for (int i = 0; i <n; i++) {
st.nextToken();
arr[i]=(int)st.nval;
}
if(k<n)
System.out.println(getNOk(0,n-1,k));
}
static int getNOk(int start,int end,int k){
while(true){
int left=start,right=end,standard=arr[start];
while(left<right){
while (left<right&&arr[right]>=standard){//要右边先走
right--;
}
while(left<right&&arr[left]<=standard){
left++;
}
if(left<right){
int v=arr[left];
arr[left]=arr[right];
arr[right]=v;
}
}
int standardi=left;
arr[start]=arr[left];
arr[left]=standard;
if(standardi<k){
start=left+1;
}
else if(standardi>k){
end=left-1;
}
else {
return standard;
}
}
}
}
递归写法代码
import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.StreamTokenizer;
public class Main {
static int[] arr;
static int n,k;
public static void main(String[] args)throws Exception {
StreamTokenizer st = new StreamTokenizer(new BufferedReader(new InputStreamReader(System.in)));
st.nextToken();
n=(int)st.nval;
st.nextToken();
k=(int)st.nval;
arr=new int[n+1];
for (int i = 0; i <n; i++) {
st.nextToken();
arr[i]=(int)st.nval;
}
if(k<n)
System.out.println(getNOk(0,n-1,k));
}
static int getNOk(int start,int end,int k){
int left=start,right=end,standard=arr[start];
while(left<right){
while (left<right&&arr[right]>=standard){
right--;
}
while(left<right&&arr[left]<=standard){
left++;
}
if(left<right){
int v=arr[left];
arr[left]=arr[right];
arr[right]=v;
}
}
int standardi=left;
arr[start]=arr[left];
arr[left]=standard;
if(standardi<k){
return getNOk(left+1,end,k);
}
else if(standardi>k){
return getNOk(start,left-1,k);
}
else {
return standard;
}
}
}
代码实现(C语言)
#include<stdio.h>
int arr[5000002];
int n,k;
int getNOk(int start,int end,int k){
while(true){
int left=start,right=end,standard=arr[start];
while(left<right){
while (left<right&&arr[right]>=standard){
right--;
}
while(left<right&&arr[left]<=standard){
left++;
}
if(left<right){
int v=arr[left];
arr[left]=arr[right];
arr[right]=v;
}
}
int standardi=left;
arr[start]=arr[left];
arr[left]=standard;
if(standardi<k){
start=left+1;
}
else if(standardi>k){
end=left-1;
}
else {
return standard;
}
}
}
int main(){
scanf("%d%d",&n,&k);
for (int i = 0; i <n; i++) {
scanf("%d",&arr[i]) ;
}
if(k<n)
printf("%d\n",getNOk(0,n-1,k));
}