1973 年,Blum、Floyd、Pratt、Rivest、Tarjan一起发布了一篇名为 “Time bounds for selection” 的论文,给出了一种在数组中选出第k大元素平均复杂度为O(N)的算法,俗称”中位数之中位数算法”。
其思想是基于对快速排序划分过程pivot的值得优化,BFPTR算法选取每5分中位数的中位数进行划分(中位数的中位数算法)
过程:
代码实现:
#include
#include
const int N = 100;
using namespace std;
bool IsBigger(int a, int b)
{
return a > b;
}
int Find_mid(int a[], int left, int right) //查找中位数的中位数
{
if (left == right)
return a[left];
int n = 0;
int i = 0;
auto it = a + left;
//该判断条件决定该for循环只处理个数为5的组,且保证个数小于5的组有且只有一组
for (i = left; i < right - 5; i += 5,it+=5)
{
sort(it, it + 4, IsBigger); //每5个数按降序排序
n = i - left; //下标为i的数在序列中占第几个位置
std::swap(a[left + n / 5], a[i+2]); //将该组数的中位数与序列前排位置交换
//n/5控制前排下标为n/5的与中位数交换
} //循环之后序列前排即是每组数的中位数,有(right-left)/5上取整个
int num = right - i + 1;
//num是个数小于5的组
//处理该组数据
if (num > 0)
{
sort(it, it + num - 1, IsBigger); //降序排序
n = i - left; //同上
//该组数给奇数个,取中位数,偶数个,取较大的数交换
std::swap(a[left + n / 5], a[i + num / 2]);
}
n /= 5; //前排中位数的个数
if (n <= 1) //剩余两个中位数,直接返回较小的
return a[left];
else //超过两个中位数,则继续在中位数中寻找中位数
return Find_mid(a, left, left + n);
}
int Find_pos(int a[], int left, int right,int mid)
{ //返回该中位数的中位数在a中的下标
for (int i=left;i!=right+1;i++)
{
if (mid == a[i])
return i;
}
}
int Partion(int a[], int left, int right,int pos) //进行划分,类似快速排序的划分过程
{
std::swap(a[pos], a[left]); //把中心数放到序列最前面
int i = left, j = right;
int pivot = a[left]; //pivot为中心数的值
while (i < j) //进行划分过程
{
while (i < j && a[j] > pivot) //从后向前找比pivot小的
j--;
a[i] = a[j];
while (i < j && a[i] < pivot) //从前向后找比pivot大的
i++;
a[j] = a[i];
}
//划分结束后,pivot前面的都是比pivot小的,pivot后面都是比pivot大的
a[i] = pivot; //确定pivot在序列中的位置
return i; //将pivot的下标返回
}
int BFPTR(int a[], int left, int right, int k) //BFPTR算法
{
int mid = Find_mid(a, left, right); //get中心数
int pos = Find_pos(a, left, right, mid); //get中心数的下标
int i = Partion(a, left, right, pos); //get划分后的中心数的下标
int m = i - left + 1; //确定中心数在该序列中为第m小的数
if (k == m)
return a[i];
if (k < m) //在小的区域进行递归查找
BFPTR(a, left, i - left, k);
else //在大的区域进行递归查找
//且要查找的为第k-m小的数
BFPTR(a, i + 1, right, k-m);
}
int main(int argc, char** argv)
{
int a[N]={ 9,6,3,8,5,2,7,4,1,0 };
cout << BFPTR(a, 0, 9, 2);
return 0;
}
结果:1
该算法最坏情况下的时间复杂度为O(n)