1. 程式人生 > >分治演算法求N個數中第K小(大)的數

分治演算法求N個數中第K小(大)的數

這個學期開演算法課,跟著進度寫寫程式碼就好。這周講分治,說到了求N個數中第K小(大)數的問題,寫寫看。

分治演算法的複雜度是O(n),用到了快速排序的思路:先選取一個參考數進行一次快排,拿升序來說的話,快排之後左邊所有數<參考數,右邊所有數>參考數,然後根據左右部分各自包含元素的個數,計算要求的元素在哪個區間內(要求的元素或是左區間中的第k小數,或是右區間中第[k-j]小數,j為左區間的長度),然後遞迴求解。

本來pku 2104和pku 2761都是求第K小數的題,無奈用這個演算法TLE,只能用後面的劃分樹或是其他複雜度更低的演算法來做,以後再說吧。我這裡只貼用這個演算法做的,雖然交上去會TLE...

#include <iostream>
 
using namespace std;
 
int partition(int[], int, int);
int Select(int[], int, int, int); 
 
int main()
{
	int n, m;
	int l, r, k;
	int v[100002];
	int d[100002];
	cin >> n >> m;
	for (int i = 1; i <= n; i++) cin >> v[i];
	while (m--)
	{
		cin >> l >> r >> k;
		for (int j = l; j <= r; j++) d[j] = v[j];
		cout << Select(d, l, r, k) << endl;
	}
 	return 0;
}
 
//一次快速排序 
int partition(int s[], int l, int r)
{
	if (l < r)
	{
		int i = l;
		int j = r;
		int x = s[i];
		while (i < j)
		{
			while (i < j && s[j] >= x) j--;
			if (i < j) s[i++] = s[j];
			while (i < j && s[i] < x) i++;
			if (i < j) s[j--] = s[i];
		}
		s[i] = x;
		return i;
	}
	return -1;
}
 
//分治找K-th Number 
int Select(int s[], int l, int r, int k)
{
	if (l > r) return -1;
	if (l == r) return s[l];
	//得到中間數的下標 
	int i = partition(s, l, r);
	//j為左區間長度 
	int j = i - l + 1;
	//位置大就在左區間找,否則就在右區間找
	if (j == k) return s[i];
	else if (j > k) return Select(s, l, i, k);
	else return Select(s, i+1, r, k-j);
}