经典算法 BFPRT算法详解
内容:
1、原始问题 =》 O(N*logN)
2、BFPRT算法 =》 O(N)
1、原始问题
问题描述:给你一个整型数组,返回其中第K小的数
普通解法:
这道题可以利用荷兰国旗改进的 partition 和随机快排的思想:随机选出一个数,将数组以该数作比较划分为 <,=,> 三个部分,
则 = 部分的数是数组中第几小的数不难得知,接着对 < (如果第K小的数在 < 部分)或 > (如果第K小的数在 > 部分)部分的数
递归该过程,直到 = 部分的数正好是整个数组中第K小的数。这种做法不难求得时间复杂度的数学期望为 O(NlogN) (以2为底)。
但这毕竟是数学期望,在实际工程中的表现可能会有偏差(最坏情况下的时间复杂度会达到O(N^2))
BFPRT算法可以说是这种算法的一种优化吧,故在此就不写这种解法的代码了
另外一种普通解法:
用堆去做,时间复杂度是靠谱的O(N*logk)
代码如下:
// 大根堆比较器
public static class MaxheapComparator implements Comparator<Integer> {
@Override
public int compare(Integer o1, Integer o2) {
return o2 - o1;
}
} // O(N*logk)的解法
public static PriorityQueue getMinKNumsByHeap(int[] arr, int k) {
if (k < 1 || k > arr.length) {
return null;
}
PriorityQueue<Integer> kHeap = new PriorityQueue<Integer>(k,
new MaxheapComparator());
for (int i = 0; i != k; i++) {
kHeap.add(arr[i]);
}
for (int i = k; i != arr.length; i++) {
if (arr[i] < kHeap.peek()) {
kHeap.poll();
kHeap.add(arr[i]);
}
}
return kHeap;
} public static void main(String[] args) {
int[] arr = { 1, 3, 2, 5, 9 };
// 测试普通方法
System.out.println(getMinKNumsByHeap(arr, 1).peek());
System.out.println(getMinKNumsByHeap(arr, 2).peek());
System.out.println(getMinKNumsByHeap(arr, 3).peek());
System.out.println(getMinKNumsByHeap(arr, 4).peek());
System.out.println(getMinKNumsByHeap(arr, 5).peek());
}
2、BFPRT算法
BFPRT算法能够做到时间复杂度就是 O(N) BFPRT算法,接收一个数组和一个K值,返回数组中的一个数
1. 数组被划分为了 N/5 个小部分,每个部分的5个数排序需要 O(1) ,所有部分排完需要 O(N/5)=O(N)
2. 取出每个小部分的中位数,一共有 N/5 个,递归调用BFPRT算法得到这些数中第 (N/5)/2 小的数(即这些数 的中位数),记为 pivot
3. 以 pivot 作为比较,将整个数组划分为 <pivot , =pivot , >pivot 三个区域
4. 判断第K小的数在哪个区域,如果在 = 区域则直接返回 pivot ,如果在 < 或 > 区域,则将这个区域的数递 归调用BFPRT算法
5. base case :在某次递归调用BFPRT算法时发现这个区域只有一个数,那么这个数就是我们要找的数
// O(N)的解法
public static int getMinKthNum(int[] arr, int k){
if(arr==null||k>arr.length){
return Integer.MIN_VALUE;
}
int[] copyArr = Arrays.copyOf(arr, arr.length);
return BFPRT(copyArr, 0, arr.length-1, k-1);
} private static int[] partition(int[] arr, int begin, int end, int pivot){
int L = begin-1;
int R = end + 1;
int cur = begin;
while(cur!=R){
if(arr[cur]>pivot){
swap(arr, cur, --R);
} else if(arr[cur]<pivot){
swap(arr, cur++, ++L);
} else{
cur++;
}
}
return new int[]{L+1, R-1};
} private static int BFPRT(int[] arr, int begin, int end, int i) {
if (begin == end) {
return arr[begin];
}
int pivot = medianOfMedians(arr, begin, end);
int[] pivotRange = partition(arr, begin, end, pivot);
if(i>=pivotRange[0]&&i<=pivotRange[1]){
return arr[i];
} else if(i<pivotRange[0]){
return BFPRT(arr, begin, pivotRange[0]-1, i);
} else{
return BFPRT(arr, pivotRange[1] + 1, end, i);
}
} private static int medianOfMedians(int[] arr, int begin, int end) {
int num = end - begin + 1;
int offset = num % 5 == 0 ? 0 : 1;
int[] medians = new int[num / 5 + offset];
for (int i = 0; i < medians.length; i++) {
int beginI = begin + i * 5;
int endI = beginI + 4;
medians[i] = getMedian(arr, beginI, Math.min(endI, end));
}
return BFPRT(medians, 0, medians.length - 1, medians.length / 2);
} private static int getMedian(int[] arr, int begin, int end){
insertionSort(arr, begin, end);
int sum = end + begin;
int mid = (sum/2) + (sum%2);
return arr[mid];
} private static void insertionSort(int[] arr, int begin, int end){
if(begin>=end){
return;
}
for(int i=begin+1;i<=end;i++){
for(int j=i;j>begin;j--){
if(arr[j]<arr[j-1]){
swap(arr, j, j-1);
} else{
break;
}
}
}
} private static void swap(int[]arr , int i, int j){
int tmp = arr[i];
arr[i] = arr[j];
arr[j] = tmp;
}
经典算法 BFPRT算法详解的更多相关文章
- JVM垃圾回收算法及回收器详解
引言 本文主要讲述JVM中几种常见的垃圾回收算法和相关的垃圾回收器,以及常见的和GC相关的性能调优参数. GC Roots 我们先来了解一下在Java中是如何判断一个对象的生死的,有些语言比如Pyth ...
- 【机器学习】【条件随机场CRF-2】CRF的预测算法之维特比算法(viterbi alg) 详解 + 示例讲解 + Python实现
1.CRF的预测算法条件随机场的预测算法是给定条件随机场P(Y|X)和输入序列(观测序列)x,求条件概率最大的输出序列(标记序列)y*,即对观测序列进行标注.条件随机场的预测算法是著名的维特比算法(V ...
- 最短路径Floyd算法【图文详解】
Floyd算法 1.定义概览 Floyd-Warshall算法(Floyd-Warshall algorithm)是解决任意两点间的最短路径的一种算法,可以正确处理有向图或负权的最短路径问题,同时也被 ...
- openerp经典收藏 字段定义详解(转载)
字段定义详解 原文地址:http://shine-it.net/index.php/topic,2159.0.htmlhttp://blog.sina.com.cn/s/blog_57ded94e01 ...
- openerp经典收藏 对象定义详解(转载)
对象定义详解 原文地址:http://shine-it.net/index.php/topic,2159.0.htmlhttp://blog.sina.com.cn/s/blog_57ded94e01 ...
- 一个经典的 HTTP协议详解
1引言 HTTP是一个属于应用层的面向对象的协议,由于其简捷.快速的方式,适用于分布式超媒体信息系统.它于1990年提出,经过几年的使用与发展,得到不断地完善和扩展.目前在WWW中使用的是HTTP/1 ...
- c++ LeetCode(初级数组篇)十一道算法例题代码详解(一)
原文作者:aircraft 原文链接:https://www.cnblogs.com/DOMLX/p/10940636.html 唉!最近忙着面试找实习,然后都是面试的很多是leetcode的算法题, ...
- KMP算法 Next数组详解
题面 题目描述 如题,给出两个字符串s1和s2,其中s2为s1的子串,求出s2在s1中所有出现的位置. 为了减少骗分的情况,接下来还要输出子串的前缀数组next.如果你不知道这是什么意思也不要问,去百 ...
- Dijkstra算法之 Java详解
转载:http://www.cnblogs.com/skywang12345/ 迪杰斯特拉算法介绍 迪杰斯特拉(Dijkstra)算法是典型最短路径算法,用于计算一个节点到其他节点的最短路径. 它的主 ...
随机推荐
- iOS-----推送机制(下)
推 送 机 制(下) 单击”从证书颁发机构请求证书”后,将会显示下图所示的对话框 输入电子邮件地址和常用名称,并选中“存储到磁盘”单选钮,然后单击“继续”按钮,该程序将会创建一个“Certificat ...
- Tensorflow 解决MNIST问题的重构程序
分为三个文件:mnist_inference.py:定义前向传播的过程以及神经网络中的参数,抽象成为一个独立的库函数:mnist_train.py:定义神经网络的训练过程,在此过程中,每个一段时间保存 ...
- CTF-练习平台-Misc之 这么多数据包
十一.这么多数据包 下载文件后解压,用wireshark打开CTF.pcapng,发现有很多包,快速浏览后发现前面都是攻击机(192.168.116.138)在向目标机(192.168.116.159 ...
- REST与RPC的简单对比
一.REST:Representational State Transfer,表述性状态转移 REST是一种架构风格,指的是一组架构约束条件和原则.满足这些约束条件和原则的应用程序或设计就是RESTf ...
- solr学习四(关于性能的杂知识)
将所有只用于搜索的,而不需要作为结果的field(特别是一些比较大的field)的stored设置为false 比如我们在solr中index了一篇word,对于这篇word,我们只需要这篇文章的下载 ...
- HIVE之 Sqoop 1.4.6 安装、hive与oracle表互导
1. sqoop数据迁移 1.1 概述 sqoop是apache旗下一款“Hadoop和关系数据库服务器之间传送数据”的工具. 导入数据:MySQL,Oracle导入数据到Hadoop的HDFS.HI ...
- Linux下C/C++代码调用PHP代码(转)
Linux下C/C++代码可以通过popen系统函数调用PHP代码并通过fgets函数获取PHP代码echo输出的字符串. //main.c char str[1024] = {0}; char * ...
- DataSet和DataTable有用的方法
每一个DataSet都是一个或多个DataTable 对象的集合(DataTable相当于数据库中的表),这些对象由数据行(DataRow).数据列(DataColumn).字段名(Column Na ...
- eclipse使用git提交项目
eclipse使用git提交项目有2种方式:一种是git命令窗口,另一种是eclipse自带git插件(跟svn插件一样使用) 一.使用git命令窗口提交项目 1.首先官网下载git并安装,然后配置用 ...
- [转]Excel.dll 导出Excel控制
Excel.dll 导出Excel控制 2010-06-12 11:26 2932人阅读 评论(2) 收藏 举报 excelmicrosoftstring产品服务器google 最近做了个导出Exce ...