线性时间找到第k小的元素:快排应用与BFPRT算法

问题分析

面对这个问题,最简单的想法是对数据进行排序,然后根据下标即可找到第k小的元素,目前已知的排序算法的最低时间复杂度为 O ( n log ⁡ 2 log ⁡ 2 n ) O(n\sqrt{\log_2 {\log_2 n}}) O(nlog2log2n ),但并不为人熟知。目前应用最广的排序算法的最低时间复杂度为 O ( n log ⁡ 2 n ) O(n\log_2 n) O(nlog2n)

但是,作为完美主义者的程序员,需要思考,找到第k小的元素一定需要排序吗?但除了寻找最大或最小的元素之外,我们似乎只能选择排序。那么能否只进行部分排序,便可找到第k小的元素呢?答案是显然的,只需要采用每一趟都能确定一个固定元素的排序算法即可。

思考 O ( n log ⁡ 2 n ) O(n\log_2 n) O(nlog2n)的排序算法中有哪些算法每一趟可以确定一个固定元素,答案是快速排序和堆排序(不考虑锦标赛排序,堆排序是锦标赛排序的升级版)。这里以快速排序为例,方便引入后面介绍的BFPRT算法,不过还有一个原因是堆排序获取第k小的元素的时间复杂度是 O ( n ) O(n) O(n)的概率小于快速排序。

快排应用

我们知道,快排每一趟都会有随机有一个元素处于最终位置上,故只需在快排中设置一个新的递归出口再加以微改便能返回第k小的元素。
代码如下:
这里采用的快排为随机化快速排序,没有了解过的的朋友可以看一下我的另外一篇博客:升级版快速排序——随机化快速排序

template<typename T>
void partition(T array[],int left,int right,int& mid)
{
	srand(time(0));
	int i = left, j = right, move = rand() % (right - left + 1) + left;
	T temp = array[move];
	array[move] = array[left];
	while (i != j)
	{
		while (array[j] > temp&&j > i) j--;
		if (i < j) array[i++] = array[j];
		while (array[i] < temp&&i < j) i++;
		if (i < j) array[j--] = array[i];

	}
	array[i] = temp;
	mid = i;
}

template<typename T>
T random_quick_sort(T array[], int left, int right,int k)
{
	
	if (k > right - left + 1) exit(7);
	int i;
	partition(array, left, right, i);
	if (k == i - left + 1) return array[i];
	else if (k > i - left + 1) return random_quick_sort(array, i + 1, right, k - i + left - 1);
	else return random_quick_sort(array, left, i - 1, k);
}

这个算法的时间复杂度的确定需要用到指示器随机变量。 E [ T ( n ) ] = E [ ∑ k = 0 n − 1 X k ( T ( m a x E[T(n)]=E[\sum_{k=0}^{n-1}X_k(T(max E[T(n)]=E[k=0n1Xk(T(max{ k , n − k − 1 k,n-k-1 k,nk1} ) ) + Θ ( n ) ] ))+\Theta(n)] ))+Θ(n)],最终得到 E [ T ( n ) ] = Θ ( n ) E[T(n)]=\Theta(n) E[T(n)]=Θ(n),即该算法的时间复杂度的期望值为 O ( n ) O(n) O(n)
但是存在最坏情况,即每一次划分只能划分出一个部分,即 T ( n ) = T ( n − 1 ) + Θ ( n ) T(n)=T(n-1)+\Theta(n) T(n)=T(n1)+Θ(n),由等差级数可以得到最坏情况下的时间复杂度为 O ( n 2 ) O(n^2) O(n2)

BFPRT算法

不想写了,困了

重写了这篇博客,新内容已放进去,转到那边去吧:找到第k小的元素【top-k】:快排应用与BFPRT算法

猜你喜欢

转载自blog.csdn.net/RealCoder/article/details/107326157
今日推荐