专栏文章

题解:P1923 求第 k 小的数

P1923题解参与者 5已保存评论 7

文章操作

快速查看文章及其快照的属性,并进行相关操作。

当前评论
5 条
当前快照
1 份
快照标识符
@mioezkzf
此快照首次捕获于
2025/12/02 18:07
3 个月前
此快照最后确认于
2025/12/02 18:07
3 个月前
查看原文

题解:P1923 【深基9.例4】求第 k 小的数

题目大意

一个大小为 n(1n5×106)n(1 \le n \le 5 \times 10^6) 的数组 aa,输出数组中第 kk 小的数(最小的数是第 00 小)。

分析

本题可以使用分治算法。分治,顾名思义就是“分而治之”,也就是把一个大问题分解成规模更小但本质相同的问题,再用同样的方法解决,再把小问题的解合并,就是大问题的解。
这一题,我们可以随便选择一个数,并将数组分为三部分:比它小的数,这个数本身,和比它大的数。然后再判断第 kk 小的数位于哪个部分,再在这个部分中继续用同样的方法查找,直到得出答案为止。平均时间复杂度可以达到 O(n)O(n)
注意最小的数是第 00 小,所以分治之前 kk 要加一。且数据范围比较大,需要用 scanfprintf 快速读入输出。

代码

可能这样说比较抽象,那就结合代码理解吧!
CPP
#include <iostream>
#include <cstdio> // 快速读入输出头文件
#include <vector> // 动态数组头文件
using namespace std;

long long n, k; // 有自定义函数,使用全局变量
long long a[5000005];

void dfs(long long l, long long r) // 自定义函数
{
	if (l > r) // 如果 l > r
	{
		return; //  直接返回
	}
	long long num = a[l];
    // 随便选一个数,为了写代码方便就选第 1 个数
	vector <long long> minn, maxn;
    // 存储比这个数大或小的数的数组,因为大小未知,所以使用动态数组
	for (long long i = l + 1; i <= r; ++i) // 遍历这一段区间
	{
		if (a[i] < num) // 将数字存入数组
		{
			minn.push_back(a[i]);
		}
		else
		{
			maxn.push_back(a[i]);
		}
	}
	for (long long i = l; i < l + minn.size(); ++i) 
	{
		a[i] = minn[i - l];
	}
    // 改变数组中数的位置,使得数组分为三部分:比 num 小的数,num 本身,和比 num 大的数
    // 注意 for 循环的范围
	a[l + minn.size()] = num;
	for (long long i = l + minn.size() + 1; i <= r; ++i)
	{
		a[i] = maxn[i - l - minn.size() - 1];
	}
	if (k < minn.size() + 1) // 分类讨论,答案在左半边
	{ 
		dfs(l, l + minn.size() - 1); // 递归
	}
	else if (k == minn.size() + 1) // 答案正好是 num
	{
		printf("%lld", num); // 输出答案,注意使用 printf 更快
		exit(0); // 直接退出程序
	}
	else // 答案在右半边
	{ 
		k -= minn.size() + 1; // 改变 k 的值
		dfs(l + minn.size() + 1, r); // 递归
	}
}

int main()
{
	scanf("%lld %lld", &n, &k); // 输入,注意使用 scanf 更快
	for (long long i = 1; i <= n; ++i)
	{
		scanf("%lld", &a[i]);
	}
	k += 1; // 题目说最小的数是第 0 小
	dfs(1, n); // 搜索并递归
	
	return 0; // 完结撒花!!!!!
}
这道题还可以直接用 sort 排序,或者用题面提到的 nth_element 函数,但本题的考点是分治算法,这里就不赘述其他方法了。
你可以
点个赞再走吧!
注意
不要抄题解,会棕名的!

评论

7 条评论,欢迎与作者交流。

正在加载评论...