BFPRT算法:

1.介绍:

BFPRT算法又叫中位数的中位数算法,主要用于在无序数组中寻找第K大或第K小的数,它的最坏时间复杂度为O(n),它是由Blum,Floyd,Pratt,Rivest,Tarjan提出,它的思想是修改快速选择算法(快排)的主元选取方法,提高在最坏情况下的时间复杂度。

2.具体方法:

BFPRT算法主要由两部分组成:快排基准选取函数。基准选取函数就是中位数的中位数算法的实现,具体来说--就是讲快排的基准选取策略进行了优化,改为每次尽可能的选择中位数作为基准。

所以说算法的核心就是通过基准选取函数找一个合理的划分值,然后就是快排的Partition过程,判断等于区域(利用区域的下标进行判断)是否命中k,否则向两边其中一边递归。

实现过程

1.将给定的数组‘arr[N]’划分为多个小组,每5个一组,小于5个的单独成组,只是在逻辑上对数组进行了分组,时间复杂度为O(N)
2.每个组进行组内排序,对5个数的排序时间是O(1),只保证组内有序,共有N/5个组,时间复杂度:O(1)*N/5=O(N).
3.得到每个组的“上中位数”,在组成新的数组newarr[](未必有序),长度是N/5。
[上中位数]:
对于奇数个数,就是中位数,比如 1 2 3 4 5,中位数:3
对于偶数个数,为前一个数,比如 1 2 3 4 ,中位数为:2
4.然后求得newarr[]的中位数,即中位数的中位数mm,作为划分值。
5.Partition过程:时间复杂度:O(N).
6.判断快排后左右指针重合的位置i+1是否等于k,大于则向右递归,小于则向左递归。

看完算法的过程,我们知道主要有这些函数:1.求中位数,2.Partition函数,3.插入排序函数(被求中位数函数调用),4.求key(即中位数的中位数)。

#include <iostream>
#include <vector>
using namespace std; int GetMedian(vector<int>a,int begin, int end);
int medianOfMedians(vector<int>a, int begin, int end);
void InsertSort(vector<int>&a,int begin, int end);
int select(vector<int>&a, int begin, int end, int K);
int Get_MinKnum_By_BFPRT(vector<int>&a,int K);
vector<int> Partition(vector<int>&a, int l,int r, int pKey); int main()
{
printf("初始数组a中的元素:");
for(int i = 0; i<a.size();++i)
cout<<a[i]<<" ";
cout<<"\n\n"; printf(" Get_MinKnum_By_BFPRT 获得的第5大的数是:%d\n\n",Get_MinKnum_By_BFPRT(a,5)); printf("用于检验:数组a排序后的元素:");
InsertSort(a,0,a.size()-1);
for(int i = 0; i<a.size();++i)
cout<<a[i]<<" ";
cout<<"\n\n"; return 0;
} //插入排序(为了求取中位数)
void InsertSort(vector<int>&a,int begin,int end)
{
if(begin == end) return;
for (int i = begin+1; i != end+1; ++i)
{
for (int j = i - 1; j >= begin ; j--)
{
if(a[j+1] < a[j])
swap(a[j],a[j+1]);
else
break;
}
}
}
//获取中位数
int GetMedian(vector<int>a,int begin, int end)
{
InsertSort(a,begin,end);
int sum = begin+end;
int mid = (sum/2) + (sum%2);
return a[mid];
} //Partition过程
vector<int> Partition(vector<int>&a, int l,int r, int pKey)
{
int less = l-1;
int more = r+1;
int pos = l;
while(pos < more)
{
if(a[pos] < pKey){
swap(a[++less],a[pos++]);
}else if (a[pos] > pKey){
swap(a[--more],a[pos]);
}else{
pos++;
}
}
std::vector<int> range;
range.push_back(less+1);
range.push_back(more-1);
return range;
} //求取划分值pKey,中位数数组的中位数
int medianOfMedians(vector<int>a, int begin, int end)
{
int num = end-begin+1;
int offset = num % 5 == 0 ? 0 : 1; //用于不足5个元素自成一组
std::vector<int> newarr(num/5+offset);
for (int i = 0; i < newarr.size(); ++i)
{
int beginI = begin + i*5;
int endI = beginI + 4;
//GetMedian()是获取每组的中位数,之后存到新数组中
newarr[i] = GetMedian(a,beginI,min(end,endI)); //取min值是因为要处理不足5个一组的情况
}
return select(newarr,0,newarr.size()-1,newarr.size()/2); //获取newarr[]的中位数
//递归的调用自己求上中位数
} //select函数:给定一个数组和范围,求位于第k位置上的数
int select(vector<int>&a, int begin, int end, int K)
{
if(begin == end)
return a[begin];
int pKey = medianOfMedians(a,begin,end);
vector<int> range = Partition(a,begin,end,pKey); if (K >= range[0] && K <= range[1]){
return a[K];
}
else if (K < range[0]) {
return select(a, begin, range[0]-1, K);
}
else {
return select(a, range[1] + 1, end, K);
}
} int Get_MinKnum_By_BFPRT(vector<int>&a,int K)
{
return select(a,0,a.size()-1,K-1);
}

读到这里大家肯定还是一知半解,很多地方还是云里雾里,例如为甚吗一定要将数组划分为N/5,这个我也不是很明白,有兴趣的可以看看BFPRT算法原理,讲的更加深入.

BFPRT算法(求第K小的数字)的更多相关文章

  1. 算法导论学习之线性时间求第k小元素+堆思想求前k大元素

    对于曾经,假设要我求第k小元素.或者是求前k大元素,我可能会将元素先排序,然后就直接求出来了,可是如今有了更好的思路. 一.线性时间内求第k小元素 这个算法又是一个基于分治思想的算法. 其详细的分治思 ...

  2. 数组中第K小的数字(Google面试题)

    http://ac.jobdu.com/problem.php?pid=1534 题目1534:数组中第K小的数字 时间限制:2 秒 内存限制:128 兆 特殊判题:否 提交:1120 解决:208 ...

  3. 九度OJ 1534 数组中第K小的数字 -- 二分查找

    题目地址:http://ac.jobdu.com/problem.php?pid=1534 题目描述: 给定两个整型数组A和B.我们将A和B中的元素两两相加可以得到数组C. 譬如A为[1,2],B为[ ...

  4. 九度OJ 题目1534:数组中第K小的数字(二分解)

    题目链接:点击打开链接 题目描述: 给定两个整型数组A和B.我们将A和B中的元素两两相加可以得到数组C. 譬如A为[1,2],B为[3,4].那么由A和B中的元素两两相加得到的数组C为[4,5,5,6 ...

  5. 九度 1534:数组中第K小的数字(二分法变形)

    题目描述: 给定两个整型数组A和B.我们将A和B中的元素两两相加可以得到数组C.譬如A为[1,2],B为[3,4].那么由A和B中的元素两两相加得到的数组C为[4,5,5,6].现在给你数组A和B,求 ...

  6. 题目1534:数组中第K小的数字 ——二分

    http://ac.jobdu.com/problem.php?pid=1534 给定两个整型数组A和B.我们将A和B中的元素两两相加可以得到数组C.譬如A为[1,2],B为[3,4].那么由A和B中 ...

  7. 九度oj 题目1534:数组中第K小的数字

    题目描述: 给定两个整型数组A和B.我们将A和B中的元素两两相加可以得到数组C. 譬如A为[1,2],B为[3,4].那么由A和B中的元素两两相加得到的数组C为[4,5,5,6]. 现在给你数组A和B ...

  8. 求第k小的数

    题目链接:第k个数 题意:求n个数中第k小的数 题解: //由快速排序算法演变而来的快速选择算法 #include<iostream> using namespace std; const ...

  9. 树状数组求第k小的元素

    int find_kth(int k) { int ans = 0,cnt = 0; for (int i = 20;i >= 0;i--) //这里的20适当的取值,与MAX_VAL有关,一般 ...

随机推荐

  1. 合并cookie,提取json数据

    发送的第3个请求需要前两个请求的cookie,需要对cookie进行合并 发送的请求数据来自于json数据中的某个键值. 这里是删除所有的对话主题目录,每一个目录有一个id,发起删除对话主题目录的请求 ...

  2. Sublime Text2 使用心得总结

    sublime text2是开发代码编辑的神器 ,编辑器界面优美,操作速度快速.而且Sublime Text2是一款跨平台的编辑器,再也不用为换平台而找不到合适的.熟悉的编辑器担忧了. Sublime ...

  3. unittest(20)- 自动更新表格中的测试数据(1)

    # 直接给出第一个手机号,而不是从excel中取数据 from openpyxl import load_workbook from tools.read_config import ReadConf ...

  4. vue基础指令了解补充及组件介绍

    v-once指令 """ v-once:单独使用,限制的标签内容一旦赋值,便不可被动更改(如果是输入框,可以主动修改) """ <di ...

  5. jQuery的html(),text()和val()比较

    .html()用为读取和修改元素的HTML标签: .text()用来读取或修改元素的纯文本内容: .val()用来读取或修改表单元素的value值: 一看黑体的部分,所以把text和html分为一组, ...

  6. The Integers and the Real Numbers

    以上我們談了一些 邏輯的基礎,接下來我們會談一些 數學的基礎,也就是整數與實數系統.其實我們已經用了很多,非正式地,接下來我們會正式地討論他們. 要 建構 實數系統的一個方法就是利用公理跟集合論來建構 ...

  7. Rust入坑指南:朝生暮死

    今天想和大家一起把我们之前挖的坑再刨深一些.在Java中,一个对象能存活多久全靠JVM来决定,程序员并不需要去关心对象的生命周期,但是在Rust中就大不相同,一个对象从生到死我们都需要掌握的很清楚. ...

  8. Linux +Docker +Nginx 部署代理转发初探

    很多开发人员仅仅只会码代码,如果让开发人员部署一些深入点的东西,或者做负载均衡等,很多开发人员估计就懵逼了,作为一个专业的开发还需要懂一些基本的运维知识:虽然说在互联网行业中,中小企业都有一个牛逼的运 ...

  9. C语言入门理解指针

    本文章为本人原创,适合于刚入坑C语言,对于指针的定义和用法模糊不清的同学,如有不正,请各位指出. 从根本来说,指针变量也是变量,只是int变成了int *,以此类推.只不过指针变量里面放的内容是普通变 ...

  10. 输入URL到浏览器显示页面的过程,搜集各方面资料总结一下

    面试中经常会被问到这个问题吧,唉,我最开始被问到的时候也就能大概说一些流程.被问得多了,自己就想去找找这个问题的全面回答,于是乎搜了很多资料和网上的文章,根据那些文章写一个总结. 写得不好,或者有意见 ...