-# 期望线性时间查找数量超过数组长度一半的数
思路分析
事实上,数量超过数组长度(n)一半的数也就是数组中第n/2大的数。
这道题如果不限制时间复杂度,brute force(通过排序)查找的时间复杂度可达到
O(nlogn)
。为了找到更快的算法,我们可以借用快速排序中的partition函数。每次运行partition函数,某一组数的pivot都能被放在最终的位置上。假设这个pivot的所处的位置是i,那么它就是整个数组中第i大的数。
递推关系
本题的详细证明请参考《算法导论》9.2节109页—以期望线性时间做选择。
代码
注:本程序是一个随机算法,利用随机数生成器期望选择时间可以达到平均水平。
#include <iostream>
#include <cstdlib>
#include <vector>
using namespace std;
int partition(vector<double> & A, int p, int r)
{
double x = A.at(r);
int i = p-1;
for(int j=p; j<r; j++)
{
if(A.at(j) <= x)
{
i++;
double tmp = A.at(j);
A.at(j) = A.at(i);
A.at(i) = tmp;
}
}
A.at(r) = A.at(i+1);
A.at(i+1) = x;
return i+1;
}
int rand_partition(vector<double> & A, int p, int r)
{
srand( (int) time(0) );
int i = rand() % (r - p + 1) + p;
double tmp = A.at(r);
A.at(r) = A.at(i);
A.at(i) = tmp;
return partition(A, p, r);
}
double rand_select(vector<double> & A, int p, int r, int i)
{
if (p == r)
{
return A.at(p);
}
int q = rand_partition(A, p, r);
int k = q - p + 1;
if (i == k)
{
return A.at(q);
}
else
{
if (i < k)
{
return rand_select(A, p, q-1, i);
}
else
{
return rand_select(A, q+1, r, i-k);
}
}
}
int main(int argc, char ** argv)
{
double arr[] = {2, 4, 2, 8, 2, 4, 2, 1, 2};
vector<double> A(arr, arr+9);
double major = rand_select(A, 0, A.size()-1, A.size()/2);
cout<<"major: "<<major<<endl;
return 0;
}
分析
这种解法只是在平均情况下,以期望线性时间的复杂度求得结果。利用哈希表(map数据结构)也可以在O(n)时间求解。