洛谷 P1923 【深基9.例4】求第 k 小的数 (Java/C)

文章介绍了如何使用快速排序的变体来寻找给定序列中的第k小的数,强调不使用nth_element函数,重点在于练习分治算法。提供了Java和C语言的代码实现,包括非递归和递归两种方法。并给出了样例输入和输出,以及处理输入数据的注意事项。

【深基9.例4】求第 k 小的数

题目链接:【深基9.例4】第k小的数

题目描述

输入 n n n 1 ≤ n < 5000000 1 \le n < 5000000 1n<5000000 n n n 为奇数)个数字 a i a_i ai 1 ≤ a i < 10 9 1 \le a_i < {10}^9 1ai<109),输出这些数字的第 k k k 小的数。最小的数是第 0 0 0 小。

请尽量不要使用 nth_element 来写本题,因为本题的重点在于练习分治算法。
样例输入

5 1
4 3 2 1 5

样例输出

2

思路

回顾快排:

对于快速排序,每一轮次的交换后都会使基准数到达它最终应该在的位置。

我们可以通过基准数最终在的位置来选择下一轮交换在哪半子数列中进行(ps:另一半的子数列就不用在排序了) 。
①如果一轮交换后基准数位置大于k,就从左半数列中运用快排的下一轮交换(因为第k小的数应该在基准点的左边)。然后得到下一轮基准数最终的位置,看是否是k,是则返回基准点的值。
②如果<k,就从右半子数列中运用快排…
重复①②直到得到最终的位置是k的基准数。

代码实现(java)

注意点:

  1. 使用Scanner读取数据会超时!
    图片超时

  2. 如果使用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]) ;
	 }

洛谷1923MLE
可以使用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));
}

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值