BFPRT

在一大堆数中求其前k大或前k小的问题,简称TOP-K问题。而目前解决TOP-K问题最有效的算法即是BFPRT算法,其又称为中位数的中位数算法,该算法由Blum、Floyd、Pratt、Rivest、Tarjan提出,最坏时间复杂度为O(n)O(n)。

读者要会快速排序相关知识,如果不会请看这里:

https://blog.csdn.net/hebtu666/article/details/81434236排序,大家在里面找快速排序阅读即可。

 

我们以前写过快排的改进求前k大或前k小,但是快排不可避免地存在退化问题,即使我们用了随机数等优化,最差情况不可避免的退化到了O(N^2),而BFPRT就解决了这个问题,主要的思想精华就是怎么选取划分值。

我们知道,经典快排是选第一个数进行划分。而改进快排是随机选取一个数进行划分,从概率上避免了基本有序情况的退化。而BFPRT算法选划分值的规则比较特殊,保证了递归最小的缩减规模也会比较大,而不是每次缩小一个数。

这个划分值如何划分就是重点。

如何让选取的点无论如何都不会太差。

1、将n个元素划分为n/5个组,每组5个元素
2、对每组排序,找到n/5个组中每一组的中位数; 
3、对于找到的所有中位数,调用BFPRT算法求出它们的中位数,作为划分值。

下面说明为什么这样找划分值。

我们先把数每五个分为一组。

同一列为一组。

排序之后,第三行就是各组的中位数。

我们把第三行的数构成一个数列,递归找,找到中位数。

这个黑色框为什么找的很好。

因为他一定比A3、B3大,而A3、B3、C3又在自己的组内比两个数要大。

我们看最差情况:求算其它的数都比c3大,我们也能在25个数中缩小九个数的规模。大约3/10.

我们就做到了最差情况固定递减规模,而不是可能缩小的很少。

下面代码实现:

public class BFPRT {
//前k小public static int[] getMinKNumsByBFPRT(int[] arr, int k) {if (k < 1 || k > arr.length) {return arr;}int minKth = getMinKthByBFPRT(arr, k);int[] res = new int[k];int index = 0;for (int i = 0; i != arr.length; i++) {if (arr[i] < minKth) {res[index++] = arr[i];}}for (; index != res.length; index++) {res[index] = minKth;}return res;}
//第k小public static int getMinKthByBFPRT(int[] arr, int K) {int[] copyArr = copyArray(arr);return select(copyArr, 0, copyArr.length - 1, K - 1);}public static int[] copyArray(int[] arr) {int[] res = new int[arr.length];for (int i = 0; i != res.length; i++) {res[i] = arr[i];}return res;}
//给定一个数组和范围,求第i小的数public static int select(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 select(arr, begin, pivotRange[0] - 1, i);} else {return select(arr, pivotRange[1] + 1, end, i);}}
//在begin end范围内进行操作public static int medianOfMedians(int[] arr, int begin, int end) {int num = end - begin + 1;int offset = num % 5 == 0 ? 0 : 1;//最后一组的情况int[] mArr = new int[num / 5 + offset];//中位数组成的数组for (int i = 0; i < mArr.length; i++) {int beginI = begin + i * 5;int endI = beginI + 4;mArr[i] = getMedian(arr, beginI, Math.min(end, endI));}return select(mArr, 0, mArr.length - 1, mArr.length / 2);//只不过i等于长度一半,用来求中位数}
//经典partition过程public static int[] partition(int[] arr, int begin, int end, int pivotValue) {int small = begin - 1;int cur = begin;int big = end + 1;while (cur != big) {if (arr[cur] < pivotValue) {swap(arr, ++small, cur++);} else if (arr[cur] > pivotValue) {swap(arr, cur, --big);} else {cur++;}}int[] range = new int[2];range[0] = small + 1;range[1] = big - 1;return range;}
//五个数排序,返回中位数public 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];}
//手写排序public static void insertionSort(int[] arr, int begin, int end) {for (int i = begin + 1; i != end + 1; i++) {for (int j = i; j != begin; j--) {if (arr[j - 1] > arr[j]) {swap(arr, j - 1, j);} else {break;}}}}
//交换值public static void swap(int[] arr, int index1, int index2) {int tmp = arr[index1];arr[index1] = arr[index2];arr[index2] = tmp;}
//打印public static void printArray(int[] arr) {for (int i = 0; i != arr.length; i++) {System.out.print(arr[i] + " ");}System.out.println();}public static void main(String[] args) {int[] arr = { 6, 9, 1, 3, 1, 2, 2, 5, 6, 1, 3, 5, 9, 7, 2, 5, 6, 1, 9 };// sorted : { 1, 1, 1, 1, 2, 2, 2, 3, 3, 5, 5, 5, 6, 6, 6, 7, 9, 9, 9 }printArray(getMinKNumsByBFPRT(arr, 10));}
}

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/445535.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

180°舵机的使用步骤

一.步骤 1.首先查看舵机的运行参数&#xff0c;包括工作的电压和电流&#xff0c;转1&#xff08;60&#xff09;需要的脉宽是多少。 2.根据舵机提供的参数&#xff0c;算出需要的PWM的周期和脉宽的范围。 3.通过单片机或者其他数字电路产生相应的PWM波&#xff0c;便可以驱…

Qt开源项目

图像处理&#xff1a; Krita digikam inkscape 编辑器&#xff1a; LiteIDE QDevelper KDeveloper Monkey Studio TeXstudio 绘图&#xff1a; ZeGrapher QtiPlot qcustomplot QWT HotShots Inkscape 三维建模&#xff1a; QCAD FreeCAD OpenModelica LibreCAD 音乐&#xff1a…

使用Python作为计算器

数值 1.python支持基本的数学运算符&#xff0c;而且应用python你可以像写数学公式那样简单明了。 eg: >>> 2 2 4 >>> 50 - 5*6 20 >>> (50 - 5*6) / 4 5.0 >>> 8 / 5 # division always returns a floating point number 1.6 2.除法…

java整体打印二叉树

一个调的很好的打印二叉树的代码。 用空格和^v来表示节点之间的关系。 效果是这样&#xff1a; Binary Tree: v7v v6v ^5^ H4H …

前缀树

是一种哈希树的变种。典型应用是用于统计&#xff0c;排序和保存大量的字符串&#xff08;但不仅限于字符串&#xff09;&#xff0c;所以经常被搜索引擎系统用于文本词频统计。它的优点是&#xff1a;利用字符串的公共前缀来减少查询时间&#xff0c;最大限度地减少无谓的字符…

学习4层板设计

今天是第一天尝试设计四层PCB板&#xff0c;以前只画过双层板&#xff0c;所以今天花了好多时间来熟悉多层板的设计方法&#xff0c;现在做一下整理&#xff0c;也方便其他同胞少走弯路~~~我用的软件是Altium Designer 6&#xff08;AD6&#xff09;步骤如下&#xff1a; 1、随…

PCB设计的基本步骤

一.方案的设计 1.与客户沟通&#xff0c;确定电路的功能和相关设计指标&#xff08;如&#xff1a;电源&#xff0c;功耗等&#xff09;。 2.画出项目的硬件功能框图。 3.设计出多种方案&#xff0c;并对多种方案进行对比&#xff0c;最终选出最合适的方案。 4.根据上述所…

堆应用例题三连

一个数据流中&#xff0c;随时可以取得中位数。 题目描述&#xff1a;有一个源源不断地吐出整数的数据流&#xff0c;假设你有足够的空间来保存吐出的数。请设计一个名叫MedianHolder的结构&#xff0c;MedianHolder可以随时取得之前吐出所有树的中位数。 要求&#xff1a; 1…

HistCite 的使用方法

摘要 读文献自然要读精品&#xff0c;在面对一个陌生领域&#xff0c;如何才能以最快速度定位精品文献呢&#xff1f;本文将详细介绍 HistCite 的使用方法&#xff0c;结合 Web of Science 和 Endnote &#xff0c;演示如何在几个小时之内&#xff0c;对某个陌生领域的文献进行…

数组基操三连(2)

转圈打印矩阵 题目&#xff1a; 给定一个整型矩阵matrix&#xff0c;请按照转圈的方式打印它。例如&#xff1a;1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,打印结果为&#xff1a;1,2,3,4,5,12,16,15,14,13,9,5,6,7,11,10 要求&#xff1a; 额外空间复杂度为O&#xff08;1&a…

数据结构课上笔记7

介绍栈和队列基本概念和用法。 设输入序列1、2、3、4&#xff0c;则下述序列中&#xff08; &#xff09;不可能是出栈序列。【中科院中国科技大学2005】 A. 1、2、3、4 B. 4、 3、2、1 C. 1、3、4、2 D.&#xff14;、1、2、3 选…

ROC曲线与AUC值

ROC曲线与AUC值 1.概述AUC&#xff08;Area Under roc Curve&#xff09;是一种用来度量分类模型好坏的一个标准。这样的标准其实有很多&#xff0c;例如&#xff1a;大约10年前在machine learning文献中一统天下的标准&#xff1a;分类精度&#xff1b;在信息检索(IR)领域中常…

设置SSH免密码自动登录(使用别名)

每次登录服务器都要写一大串的用户名&#xff08;username服务器地址&#xff09;和登录密码十分的繁琐&#xff0c;所以本文就告诉大家如何通过修改配置文件&#xff0c;达到只需要输入&#xff1a;ssh jack(你起的别名)就可以一键登录到服务器中。 1.创建公钥&#xff08;相当…

串的定长表示

思想和代码都不难&#xff0c;和线性表也差不多&#xff0c;串本来就是数据受限的线性表。 串连接&#xff1a; #include <stdio.h> #include <string.h> //串的定长顺序存储表示 #define MAXSTRLEN 255 //用户可在255以内定义最大串长 typedef unsigned cha…

周志华《Machine Learning》 学习笔记系列(1)--绪论

机器学习致力于研究如何通过计算手段&#xff0c;利用经验来改善系统本身的性能&#xff0c;在计算机系统中&#xff0c;“经验”通常是以“数据”形式存在的&#xff0c;所以&#xff0c;机器学习的主要内容是关于在计算机上从数据中产生“模型”的算法&#xff0c;即学习算法…

轻松理解牛顿迭代法且用其求平方根

牛顿迭代法概述 牛顿迭代法&#xff08;Newton’s method&#xff09;又称为牛顿-拉弗森方法&#xff08;Newton-Raphson method&#xff09;&#xff0c;它是牛顿在17世纪提出的一种在实数域和复数域上近似求解方程的方法。 牛顿迭代公式 设rrr是f(x)0f(x)0f(x)0的根&#…

map+DP leetcode446

如果数字序列由至少三个元素组成并且任何两个连续元素之间的差异相同&#xff0c;则称为算术序列。 例如&#xff0c;这些是算术序列&#xff1a; 1&#xff0c;3&#xff0c;5&#xff0c;7&#xff0c;9 7&#xff0c;7,7&#xff0c;7 3&#xff0c;-1&#xff0c;-5&am…

如何使用cookie信息,完成自动登录

在做爬虫任务的时候&#xff0c;我们常常会遇到很多网页必须登录后&#xff0c;才可以开放某些页面。所以登录是爬取网页的第一步。但是&#xff0c;通过post表单&#xff08;包含用户名和密码&#xff09;的方法&#xff0c;对于那些不需要输入比较复杂的验证码的网页&#xf…

Spring Cloud 学习笔记(1 / 3)

Spring Cloud 学习笔记&#xff08;2 / 3&#xff09; Spring Cloud 学习笔记&#xff08;3 / 3&#xff09; ---01_前言闲聊和课程说明02_零基础微服务架构理论入门03_第二季Boot和Cloud版本选型04_Cloud组件停更说明05_父工程Project空间新建06_父工程pom文件07_复习Depend…

后缀树/后缀数组

字典树&#xff1a;https://blog.csdn.net/hebtu666/article/details/83141560 后缀树&#xff1a;后缀树&#xff0c;就是把一串字符的所有后缀保存并且压缩的字典树。 相对于字典树来说&#xff0c;后缀树并不是针对大量字符串的&#xff0c;而是针对一个或几个字符串来解决…