先说快排最坏情况下的时间复杂度为n^2。

正常情况:

最坏的情况下,待排序的记录序列正序或逆序,每次划分只能得到一个比上一次划分少一个记录的子序列,(另一个子序列为空)。此时,必须经过n-1次递归调用才能把所有记录定位,而且第i趟划分需要经过n-i次比较才能找个才能找到第i个记录的位置,因此时间复杂度为

所以BFPRT本质上是在寻找正确的pivot元素!!!避免这种最坏情况出现。

在BFPTR算法中,仅仅是改变了快速排序Partion中的pivot值的选取,在快速排序中,我们始终选择第一个元素或者最后一个元素作为pivot,而在BFPTR算法中,每次选择五分中位数的中位数作为pivot,这样做的目的就是使得划分比较合理,从而避免了最坏情况的发生。算法步骤如下:

1. 将  个元素划为  组,每组5个,至多只有一组由  个元素组成。 
2. 寻找这  个组中每一个组的中位数,这个过程可以用插入排序。 
3. 对步骤2中的  个中位数,重复步骤1和步骤2,递归下去,直到剩下一个数字。
4. 最终剩下的数字即为pivot,把大于它的数全放左边,小于等于它的数全放右边。 
5. 判断pivot的位置与k的大小,有选择的对左边或右边递归。

当你看到本质上后,至于”首先把数组按5个数为一组进行分组,最后不足5个的忽略。 ” 、“偶数个元素的中位数取中间2个中较小的一个。”这些小细节都是无关紧要的了!!!

下面为代码实现,其所求为前 k 小的数

#include <iostream>
#include <algorithm>using namespace std;int InsertSort(int array[], int left, int right);
int GetPivotIndex(int array[], int left, int right);
int Partition(int array[], int left, int right, int pivot_index);
int BFPRT(int array[], int left, int right, int k);int main()
{int k = 8; // 1 <= k <= array.sizeint array[20] = { 11,9,10,1,13,8,15,0,16,2,17,5,14,3,6,18,12,7,19,4 };cout << "原数组:";for (int i = 0; i < 20; i++)cout << array[i] << " ";cout << endl;// 因为是以 k 为划分,所以还可以求出第 k 小值cout << "第 " << k << " 小值为:" << array[BFPRT(array, 0, 19, k)] << endl;cout << "变换后的数组:";for (int i = 0; i < 20; i++)cout << array[i] << " ";cout << endl;return 0;
}/*** 对数组 array[left, right] 进行插入排序,并返回 [left, right]* 的中位数。*/
int InsertSort(int array[], int left, int right)
{int temp;int j;for (int i = left + 1; i <= right; i++){temp = array[i];j = i - 1;while (j >= left && array[j] > temp)array[j + 1] = array[j--];array[j + 1] = temp;}return ((right - left) >> 1) + left;
}/*** 数组 array[left, right] 每五个元素作为一组,并计算每组的中位数,* 最后返回这些中位数的中位数下标(即主元下标)。** @attention 末尾返回语句最后一个参数多加一个 1 的作用其实就是向上取整的意思,* 这样可以始终保持 k 大于 0。*/
int GetPivotIndex(int array[], int left, int right)
{if (right - left < 5)return InsertSort(array, left, right);int sub_right = left - 1;// 每五个作为一组,求出中位数,并把这些中位数全部依次移动到数组左边for (int i = left; i + 4 <= right; i += 5){int index = InsertSort(array, i, i + 4);swap(array[++sub_right], array[index]);}// 利用 BFPRT 得到这些中位数的中位数下标(即主元下标)return BFPRT(array, left, sub_right, ((sub_right - left + 1) >> 1) + 1);
}/*** 利用主元下标 pivot_index 进行对数组 array[left, right] 划分,并返回* 划分后的分界线下标。*/
int Partition(int array[], int left, int right, int pivot_index)
{swap(array[pivot_index], array[right]); // 把主元放置于末尾int partition_index = left; // 跟踪划分的分界线for (int i = left; i < right; i++){if (array[i] < array[right]){swap(array[partition_index++], array[i]); // 比主元小的都放在左侧}}swap(array[partition_index], array[right]); // 最后把主元换回来return partition_index;
}/*** 返回数组 array[left, right] 的第 k 小数的下标*/
int BFPRT(int array[], int left, int right, int k)
{int pivot_index = GetPivotIndex(array, left, right); // 得到中位数的中位数下标(即主元下标)int partition_index = Partition(array, left, right, pivot_index); // 进行划分,返回划分边界int num = partition_index - left + 1;if (num == k)return partition_index;else if (num > k)return BFPRT(array, left, partition_index - 1, k);elsereturn BFPRT(array, partition_index + 1, right, k - num);
}

  

运行如下:

原数组:11 9 10 1 13 8 15 0 16 2 17 5 14 3 6 18 12 7 19 4
第 8 小值为:7
变换后的数组:4 0 1 3 2 5 6 7 8 9 10 12 13 14 17 15 16 11 18 19

性能分析:

划分时以5个元素为一组求取中位数,共得到n/5个中位数,再递归求取中位数,复杂度为T(n/5)。

得到的中位数x作为主元进行划分,在n/5个中位数中,主元x大于其中1/2*n/5=n/10的中位数,而每个中位数在其本来的5个数的小组中又大于或等于其中的3个数,所以主元x至少大于所有数中的n/10*3=3/10*n个。同理,主元x至少小于所有数中的3/10*n个。即划分之后,任意一边的长度至少为3/10,在最坏情况下,每次选择都选到了7/10的那一部分,则递归的复杂度为T(7/10*n)。

在每5个数求中位数和划分的函数中,进行若干个次线性的扫描,其时间复杂度为c*n,其中c为常数。其总的时间复杂度满足 T(n) <= T(n/5) + T(7/10*n) + c * n。

我们假设T(n)=x*n,其中x不一定是常数(比如x可以为n的倍数,则对应的T(n)=O(n^2))。则有 x*n <= x*n/5 + x*7/10*n + c*n,得到 x<=10*c。于是可以知道x与n无关,T(n)<=10*c*n,为线性时间复杂度算法。而这又是最坏情况下的分析,故BFPRT可以在最坏情况下以线性时间求得n个数中的第k个数。

时间复杂度为O(n)的原因

我们选取的x可以在每次递归的时候最少淘汰(n/10-2)x3的数据量

详细的时间复杂度分析见:https://blog.csdn.net/LaoJiu_/article/details/54986553

转载于:https://www.cnblogs.com/bonelee/p/10248206.html

BFPRT 算法 (TOP-K 问题)——本质就是在利用分组中位数的中位数来找到较快排更合适的pivot元素...相关推荐

  1. 数据结构排序算法 内部排序(冒泡、鸡尾酒、选择、简单插入、二分插入、快排、希尔、归并、堆排)C语言实现

    文章目录 排序 冒泡排序 鸡尾酒排序 选择排序: 简单插入排序: 二分插入排序 快速排序: 希尔排序: 归并排序: 堆排序: 排序 点击以下图片查看大图: 冒泡排序 1.比较相邻的元素,如果前一个比后 ...

  2. 精确查找top k和非精确查找top k

    信息检索里面经典问题. 精确top K 检索及其加速办法 •方法一:快速计算余弦 • 方法二:堆排序法N中选K • 方法三:提前终止计算 精确top K检索加速方法一: 快速计算余弦 • 检索排序就是 ...

  3. 【数据结构与算法之美】排序(下):如何用快排思想在O(n)内查找第K大元素?

    目录 一.分治思想 二.归并排序 三.快速排序 四.归并排序与快速排序的区别 五.课后思考 一.分治思想 1.分治思想:分治,顾明思意,就是分而治之,将一个大问题分解成小的子问题来解决,小的子问题解决 ...

  4. (十)更快的排序算法(归并、快排、基数)

    目标 1) 使用下列方法将一个数组按升序排序:归并排序.快速排序和基数排序 2) 评估排序的效率,讨论不同的方法的相对效率 目录 9.1 归并排序 9.1.1 归并数组 9.1.2 递归归并排序 9. ...

  5. 【数据结构与算法】快排、归并 O(nlogn) 基于比较

    冒泡.插入.选择 O(n^2) 基于比较 快排.归并 O(nlogn) 基于比较 计数.基数.桶 O(n) 不基于比较 一.分治思想 1.分治思想:分治,顾明思意,就是分而治之,将一个大问题分解成小的 ...

  6. 数组中最大第K元素(快排思想)

    1. 利用快排的思想找数组中最大K元素,但是找到最大K元素 是排序后的数组 地址索引为K位置上的元素 但是利用快排的思想 不需要进行排序 只需要找到 地址索引=k 位置 前k-1个元素 不一定是排序的 ...

  7. 寻找第K大的数(快排思想)

    使用快排思想找第K大的数,算法复杂度O(n). 1.以数组a的第0位a[0]为参考基准base,将数组划分为两个部分: 如果找第K大的数,则将大于base的数往前挪,将小于base的数往后挪.如果找第 ...

  8. [Leedcode][第215题][JAVA][数组中的第K个最大元素][快排][优先队列]

    [问题描述][中等] 在未排序的数组中找到第 k 个最大的元素.请注意,你需要找的是数组排序后的第 k 个最大的元素,而不是第 k 个不同的元素.示例 1:输入: [3,2,1,5,6,4] 和 k ...

  9. 分治法在排序算法中的应用(JAVA)--快速排序(Lomuto划分、Hoare划分、随机化快排)

    分治法在排序算法中的应用--快速排序 时间复杂度:平均O(nlogn),最坏O(n^2) 如果说归并排序是按照元素在数组中的位置划分的话,那么快速排序就是按照元素的值进行划分.划分方法由两种,本节将主 ...

最新文章

  1. android usb 触摸屏 apk,Android插入USB设备,自动弹出提示运行apk
  2. AI破解脑电波,准确率超80%!高度还原你眼中最美的ta
  3. 十八、可以输出文字了
  4. Java性能优化(3):通过私有构造函数强化不可实例化的能力
  5. python创建虚拟环境jupyter_机器学习中python的有关使用技巧【创建虚拟环境、jupyter的kernel修改】...
  6. linux_从windows到ubuntu再到manjaro
  7. 《机器学习与数据科学(基于R的统计学习方法)》——2.8 读取JSON文件
  8. python 直方图匹配_python库skimage 绘制直方图;绘制累计直方图;实现直方图匹配(histogram matching)...
  9. matplotlib绘图并导出eps矢量图和svg矢量图
  10. could not find function 函数名
  11. 有关自己人事档案(学籍)怎么查询攻略
  12. ActiveMQ 反序列化漏洞 (CVE-2015-5254)复现
  13. 终于明白为什么人人都爱Django了,Django果然天下第一
  14. 关于符号Symbol第二篇
  15. 《前端圈技术论坛-腾讯互娱专场》观后感
  16. pdf转图片可调整大小分辨率
  17. STM32CubeIDE的一点使用技巧
  18. Kubernetes(K8s)基本概念:Volume(存储卷)、Persistent Volume
  19. 那些年,磕磕碰碰的BUG
  20. pstack 安装linux_linux下跟踪进程调用栈strace pstack gstack

热门文章

  1. Linux之scp命令实现远程服务器的文件拷贝
  2. 安徽计算机中专学校有哪些,安徽2021年中专学校里面都有什么专业
  3. ajax获取php cookie,Ajax 无法跨域获取 cookie
  4. winform 在panel怎么实现锚点定位_高德网络定位之“移动WiFi识别”
  5. java注册用户代码_java用户管理注册功能 含前后台代码
  6. java int interger_java面试题之int和Integer的区别
  7. 关于js选项卡的一些问题
  8. java核心技术面试精讲
  9. 【网页前端设计Front end】HTML语言基础.上(看不懂你来打我)
  10. python写了代码_Python写代码的用法建议