【极难】【BFPRT算法】找到无序数组中最小的K个数

题目:来自脑客爱刷题

给定一个无序的整型数组arr,找到其中最小的k个数。


方法一:时间复杂度O(N*logK)

O(N*logK)的解法说起来非常简单,就是一直维护一个k个数的大根堆,这个堆代表目前选出的k个最小的数,在堆里的k个元素中堆顶的元素是最小的k个数里最大的那个。在遍历整个数组的过程中,看看当前数是否比堆顶元素小:如果是,就把堆顶的元素替换成当前的数,然后从堆顶的位置调整整个堆,让替换操作后的堆的最大元素继续处在堆顶的位置;如果不是,不进行任何的操作,继续遍历下一个数。在遍历完成后,堆中的k个数就是所有数组中最小的k个数。

方法二:BFPRT算法,时间复杂度O(N)


代码如下:

//在数组[begin,end]区间内,以数字part作为区分条件,左边的数比part小,中间的数是part(如果part不止一个,则连续出现),右边的数比part大
pair<int, int> partition(vector<int> &arr, int begin, int end, int part)
{
	if (arr.empty() || begin > end || begin < 0 || end >= arr.size())
		return pair<int, int>(-1, -1);
	int smaller = begin - 1, bigger = end + 1,curindex=begin;
	while (curindex < bigger)
	{
		if (arr[curindex] < part)
		{
			swap(arr[curindex], arr[smaller + 1]);
			curindex++;
			smaller++;
		}
		else if (arr[curindex] > part)
		{
			swap(arr[curindex], arr[bigger - 1]);
			bigger--;
		}
		else
			curindex++;
	}
	return pair<int, int>(smaller+1, bigger-1);
}

//getMinKhByBFPR2()函数相当于返回arr排序后,下标为k-1的数
int getMinKhByBFPR(vector<int> &arr, int begin, int end, int k)
{
	int length = end - begin + 1;
	if (begin == end)
	{
		return arr[begin];
	}
	int size = length % 5 == 0 ? length / 5 : length / 5 + 1;
	vector<int> midnums(size);
	for (int i = 0; i < size; i++)
	{
		int beginindex = begin + i * 5;
		int endindex = min(beginindex + 4,end);
		int len = endindex - beginindex + 1;
		sort(arr.begin() + beginindex, arr.begin() + endindex + 1);
		midnums[i] = arr[beginindex+len / 2];
	}
	int midnum = getMinKhByBFPR(midnums, 0, midnums.size() - 1, midnums.size() / 2 + 1);//返回的是下中位数
	pair<int, int> r = partition(arr, begin, end, midnum);
	if (k - 1 < r.first)
		return getMinKhByBFPR(arr, begin, r.first - 1, k);
	else if (k - 1 > r.second)
		return getMinKhByBFPR(arr, r.second + 1, end, k);//注意,这里getMinKhByBFPR()的最后一个参数是k!
	else
		return midnum;
}

vector<int> getMinKNumsByBFPRT(const vector<int> &arr, int k)
{
	vector<int> res;
	if (arr.empty() || k <= 0)
		return res;
	if (k >= arr.size())
		return arr;
	vector<int> copyarr = arr;
	int MinKh = getMinKhByBFPR(copyarr, 0, arr.size() - 1, k);
	for (int i = 0; i < arr.size(); i++)
	{
		if (arr[i] < MinKh)
			res.push_back(arr[i]);
	}
	while (res.size() < k)
		res.push_back(MinKh);
	return res;
}
注意,方法二使用的partition和传统快排使用的partition不同。上面的partition可以将数字part连续出现在中间。相当于把原始数组分成三部分:左边的数比part小,中间的数等于part,右边的数大于part。快排中使用这种partition可以更快地完成排序(因为它比传统方法进一步缩小了下一轮排序的区间)。另外,某些问题可以用这种partition更快解决。比如盒子里有红黑白三色球,把它们按颜色分开,就能用这种partition完成。


相关题型:

题目:EPI

该题的特殊之处在于,输入是一种流,事先不知道数组的长度,且其长度是非常大的。

建立一个数组存储读进来的数据,当数组长度达到2K-1的时候,可用partition的方式删除较小的k-1个数。



代码:







  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值