寻找第k小个元素:特定分治策略

本文介绍了一种高效算法,用于从一组无序整数中找出第K小的元素。通过分组、排序及分治策略实现,平均时间复杂度为O(n)。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1.问题

求n个数中第k小个数。

2.解析

在这里插入图片描述

在这里插入图片描述

3.设计

#include<iostream>
#include<algorithm>
#include<math.h>
using namespace std;
int tmp(int a, int b) {  //排序函数sort的第三个参数,在该题中用于将每一列的五个数从大到小排序
	return a > b;
}
class FindK {  //定义类,表示每一列的五个数
public:
	FindK(){}
	FindK(int a) {  //因为存在小于5的列,所以有适应各种情况的构造函数
		x[0] = a;
	}
	FindK(int a, int b) {
		x[0] = a;
		x[1] = b;
	}
	FindK(int a, int b, int c) {
		x[0] = a;
		x[1] = b;
		x[2] = c;
	}
	FindK(int a, int b, int c, int d) {
		x[0] = a;
		x[1] = b;
		x[2] = c;
		x[3] = d;
	}
	FindK(int a, int b, int c, int d, int e) {
		x[0] = a;
		x[1] = b;
		x[2] = c;
		x[3] = d;
		x[4] = e;
	}
	void setX(int a) {
		x[0] = a;
		size = 1;
	}
	void setX(int a, int b) {
		x[0] = a;
		x[1] = b;
		size = 2;
	}
	void setX(int a, int b, int c) {
		x[0] = a;
		x[1] = b;
		x[2] = c;
		size = 3;
	}
	void setX(int a, int b, int c, int d) {
		x[0] = a;
		x[1] = b;
		x[2] = c;
		x[3] = d;
		size = 4;
	}
	void setX(int a, int b, int c, int d, int e) {
		x[0] = a;
		x[1] = b;
		x[2] = c;
		x[3] = d;
		x[4] = e;
		size = 5;
	}
	void sortItself(int n) {  //排序函数,将每列5个数从大到小排
		sort(x, x + n, tmp);
		mid = x[n / 2];  //选出中位数
	}
	int mid;             //中位数
	int x[5];            //代表每列的5个数
	int size;            //数的个数
};
int tmpMid(FindK a, FindK b) {  //根据中位数从小到大排序
	return a.mid < b.mid;
}
class Set {  //集合类
public:
	Set() {
		number = 0;
	}
	Set(int n) {
		number = 0;
		size = n;
	}
	void add(int x) {  //往集合中添加元素
		items[number++] = x;
	}
	int getN(int i) {
		return items[i];
	}
	int getNumber() {  //返回集合中元素个数
		return number;
	}
	int size;
	int number;      //当前下标和个数
	int items[100];  //用于存放数据
};
int find_k(int k, Set s, int size) {  //核心递归算法
	FindK x[20];
	int z = floor(size / 5.0);  //算出组数
	int i;
	for (i = 0; i < z; i++) {  //构造每一组
		x[i].setX(s.items[5 * i], s.items[5 * i + 1], s.items[5 * i + 2], s.items[5 * i + 3], s.items[5 * i + 4]);
		x[i].sortItself(5);
	}
	if (size - size / 5 * 5 == 1) {  //不足5个数的构造方法,下同
		x[i].setX(s.items[5 * i]);
		x[i].sortItself(1);
	}
	else if (size - size / 5 * 5 == 2) {
		x[i].setX(s.items[5 * i], s.items[5 * i + 1]);
		x[i].sortItself(2);
	}
	else if (size - size / 5 * 5 == 3) {
		x[i].setX(s.items[5 * i], s.items[5 * i + 1], s.items[5 * i + 2]);
		x[i].sortItself(3);
	}
	else if (size - size / 5 * 5 == 4) {
		x[i].setX(s.items[5 * i], s.items[5 * i + 1], s.items[5 * i + 2], s.items[5 * i + 3]);
		x[i].sortItself(4);
	}
	sort(x, x + z + 1, tmpMid);    //按横方向中位数从小到大排序
	int mid = x[(z + 1) / 2].mid;  //算出横方向的中位数
	Set s1, s2;                    //构造两个集合S1,S2
	for (i = 0; i < z + 1; i++) {
		if (x[i].size == 1 && i != (z + 1) / 2) {  //处理不足5个数的列,size表示该列的个数
			if (x[i].x[0] < mid)
				s1.add(x[i].x[0]);
			else
				s2.add(x[i].x[0]);
		}
		else if (x[i].size == 1 && i == (z + 1) / 2) {
			continue;
		}
		else if (x[i].size == 2 && i != (z + 1) / 2) {
			if (x[i].x[0] < mid)
				s1.add(x[i].x[0]);
			else
				s2.add(x[i].x[0]);
			if (x[i].x[1] < mid)
				s1.add(x[i].x[1]);
			else
				s2.add(x[i].x[1]);
		}
		else if (x[i].size == 2 && i == (z + 1) / 2) {
			if (x[i].x[0] < mid)
				s1.add(x[i].x[0]);
			else
				s2.add(x[i].x[0]);
		}
		else if (x[i].size == 3 && i != (z + 1) / 2) {
			if (x[i].x[0] < mid)
				s1.add(x[i].x[0]);
			else
				s2.add(x[i].x[0]);
			if (x[i].x[1] < mid)
				s1.add(x[i].x[1]);
			else
				s2.add(x[i].x[1]);
			if (x[i].x[2] < mid)
				s1.add(x[i].x[2]);
			else
				s2.add(x[i].x[2]);
		}
		else if (x[i].size == 3 && i == (z + 1) / 2) {
			if (x[i].x[0] < mid)
				s1.add(x[i].x[0]);
			else
				s2.add(x[i].x[0]);
			if (x[i].x[2] < mid)
				s1.add(x[i].x[2]);
			else
				s2.add(x[i].x[2]);
		}
		else if (x[i].size == 4 && i != (z + 1) / 2) {
			if (x[i].x[0] < mid)
				s1.add(x[i].x[0]);
			else
				s2.add(x[i].x[0]);
			if (x[i].x[1] < mid)
				s1.add(x[i].x[1]);
			else
				s2.add(x[i].x[1]);
			if (x[i].x[2] < mid)
				s1.add(x[i].x[2]);
			else
				s2.add(x[i].x[2]);
			if (x[i].x[3] < mid)
				s1.add(x[i].x[3]);
			else
				s2.add(x[i].x[3]);
		}
		else if (x[i].size == 4 && i == (z + 1) / 2) {
			if (x[i].x[0] < mid)
				s1.add(x[i].x[0]);
			else
				s2.add(x[i].x[0]);
			if (x[i].x[1] < mid)
				s1.add(x[i].x[1]);
			else
				s2.add(x[i].x[1]);
			if (x[i].x[3] < mid)
				s1.add(x[i].x[3]);
			else
				s2.add(x[i].x[3]);
		}
		else {                      //对每列有5个数的处理
			if (i <= (z + 1) / 2) {
				if (i == (z + 1) / 2) {
					s1.add(x[i].x[3]);
					s1.add(x[i].x[4]);
				}
				else {
					s1.add(x[i].x[2]);
					s1.add(x[i].x[3]);
					s1.add(x[i].x[4]);
					if (x[i].x[0] < mid)
						s1.add(x[i].x[0]);
					else
						s2.add(x[i].x[0]);
					if (x[i].x[1] < mid)
						s1.add(x[i].x[1]);
					else
						s2.add(x[i].x[1]);
				}
			}
			if (i >= (z + 1) / 2) {
				if (i == (z + 1) / 2) {
					s2.add(x[i].x[0]);
					s2.add(x[i].x[1]);
				}
				else {
					s2.add(x[i].x[0]);
					s2.add(x[i].x[1]);
					s2.add(x[i].x[2]);
					if (x[i].x[3] < mid)
						s1.add(x[i].x[3]);
					else
						s2.add(x[i].x[3]);
					if (x[i].x[4] < mid)
						s1.add(x[i].x[4]);
					else
						s2.add(x[i].x[4]);
				}
			}
		}
	}
	if (s1.getNumber() == k - 1) {  //分为找到、小于、大于三种情况来处理,后两种要分治递归处理
		return mid;
	}
	else if (s1.getNumber() > k - 1) {
		return find_k(k, s1, s1.getNumber());
	}
	else {
		return find_k(k - 1 - s1.getNumber(), s2, s2.getNumber());
	}
}
int main() {
	int n;
	int i;
	int a, b, c, d, e;
	int x;
	int k;
	cout << "输入要输入的数据个数:";
	cin >> n;
	cout << "输入第几小的数:";
	cin >> k;
	cout << "输入这" << n << "个数:" << endl;
	Set set1(n);
	for (i = 0; i < n; i++) {
		cin >> x;
		set1.add(x);
	}
	cout << find_k(k, set1, n) << endl;
	return 0;
}

测试用例

4.分析

时间复杂度:O(n)

5.源码

https://github.com/BeFlamingo/faming6

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值