【BFPTR】震惊!竟然还有比 快速排序 更快的算法...

在介绍 堆 和 加强堆 的文章中,我们探讨了当有新的元素加入时,如何实时更新前 K 个元素的方法。

(还没学习过的小伙伴赶快关注,在 「堆」 合集里查看哦!)
在这里插入图片描述

今天我们介绍一种新的方法,使用 bfptr 算法求解第 K 小(大) 的数。

BFPTR 算法

BFPRT 是一种 改进版快速排序 算法。

为什么说是改进版的快排呢?简单回顾一下 快排 的步骤:

  1. 随机选择一个元素(或者当前划分的首或尾元素)作为初始的 基准元素
  2. 遍历每个元素并与基准元素进行比较:
  • 所有比基准值 的元素放在左侧;
  • 所有比基准值 的元素放在右侧;
  • 中间位置是所有与基准元素 相等 的元素。
  1. 对左右两部分分别递归进行快速排序。

其时间复杂度为 O ( N l o g N ) O(NlogN) O(NlogN),但最坏情况下的时间复杂度退化为 O ( n 2 ) O(n^2) O(n2)

原因在于如果选择的基准值“太偏”时,两侧未排好的元素数量不均匀,起不到快排的效果,时间复杂度退化为了 O ( n 2 ) O(n^2) O(n2)


因此,选择一个好的基准元素至关重要,BFPRT算法 就是 用来选择一个较好的基准元素,避免最坏情况的产生

选取原则

  1. 将待排序的数组按 5 个元素一组进行分组,最后不足 5 个的也划分到新的一组中。
  2. 将每一个小组中的 5 个元素进行排序,并取第 3 个元素(即 5 个元素的中位数)。最后一组若有偶数个元素,取上中位数。
  3. 将得到的每组的中位数再次进行排序,取其中位数作为本次划分的基准元素。
  4. 选好基准元素后,剩下的步骤同快速排序。

因此,BFPTR 算法又叫做 中位数的中位数算法 ,其实就是对快速排序第一步中 如何选择基准元素 进行了一定的改进和优化,其它步骤与快速排序无任何差别。

提示:对于 5 个元素的排序,选择最简单的 插入排序 即可。

主要代码

// bfprt 算法
public static void minKth(int[] array, int k) {// 拷贝一份,不破坏原有数组int[] arr = copyArray(array);// 第 k 小的数,下标为 k-1int ans = bfprt(arr, 0, arr.length - 1, k - 1);System.out.println(ans);
}// 如果 arr[L..R] 有序时,返回 index 位置上的数
public static int bfprt(int[] arr, int L, int R, int index) {if (L == R) {return arr[L];}// 划分 基准元素int pivot = medianOfMedians(arr, L, R);// 对 pivot 元素进行快排划分// 返回等于 pivot 元素的范围下标[L,R]数组int[] range = partition(arr, L, R, pivot);// 下标index 在 等于 pivot 元素的范围下标[L,R] 中,直接返回if (index >= range[0] && index <= range[1]) {return arr[index];} else if (index < range[0]) {// 对左侧继续递归return bfprt(arr, L, range[0] - 1, index);} else {// 对右侧继续递归return bfprt(arr, range[1] + 1, R, index);}
}

以上部分其实就是改进过后的快速排序代码,无需对所有的元素进行排序,只需对左右其中一侧进行递归排序,直至找到下标 index (即 k-1 下标)元素。

划分代码

// 选择中位数的中位数
public static int medianOfMedians(int[] arr, int L, int R) {int size = R - L + 1;// 判断是否有不足 5 个为一组的int offset = size % 5 == 0 ? 0 : 1;int[] mArr = new int[size / 5 + offset];// 每 5 个一组的中位数计入mArr中for (int team = 0; team < mArr.length; team++) {// 计算每组的起始位置int teamFirst = L + team * 5;// 起始位置teamFirst 到结尾位置teamFirst + 4这一组进行插入排序// 若最后不足 5 个了,结尾位置为 RmArr[team] = getMedian(arr, teamFirst, Math.min(R, teamFirst + 4));}// 再求 mArr 的中位数,即 mArr.length / 2 位置上的数是几return bfprt(mArr, 0, mArr.length - 1, mArr.length / 2);
}// 返回排序后的中位数
public static int getMedian(int[] arr, int L, int R) {insertionSort(arr, L, R);return arr[(L + R) / 2];
}// 插入排序
public static void insertionSort(int[] arr, int L, int R) {for (int i = L + 1; i <= R; i++) {for (int j = i - 1; j >= L && arr[j] > arr[j + 1]; j--) {swap(arr, j, j + 1);}}
}

函数间的调用有点儿多,仔细理解每个函数的功能,相信小伙伴能够轻松搞懂代码逻辑。


该算法的时间复杂度为:

能够证明出,上述式子的时间复杂度为 O ( N ) O(N) O(N) 。具体的推导及证明过程,感兴趣的小伙伴可以参照《算法导论》 9.3节 Selection in worst-case linear time 的内容进行理解。关注回复「算法导论」 获取该书籍。

在这里插入图片描述

虽然证明出了该方法避免了最坏的时间复杂度,但其实使用最初的快速排序也不会太差,ACM算法竞赛入门这本书中提到了这样的一句话:实践中几乎不可能达到最坏情况。 O ( N ) O(N) O(N)的时间复杂度内能够求出第 k 大(小)的元素。关注回复「ACM紫书」 获取该书籍。
在这里插入图片描述

在这里插入图片描述

~ 点赞 ~ 关注 ~ 星标 ~ 不迷路 ~!!!

回复「ACM紫书」获取 ACM 算法书籍 ~
回复「算法导论」获取 算法导论第3版 ~

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

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

相关文章

【云计算】云数据中心网络(七):负载均衡

《云网络》系列&#xff0c;共包含以下文章&#xff1a; 云网络是未来的网络基础设施云网络产品体系概述云数据中心网络&#xff08;一&#xff09;&#xff1a;VPC云数据中心网络&#xff08;二&#xff09;&#xff1a;弹性公网 IP云数据中心网络&#xff08;三&#xff09;…

QT C++ sqlite 对多个数据库的操作

//本文描述&#xff0c;QT 对多数据库的操作。 //你可能会想&#xff0c;多数据库的操作时&#xff0c;查询语句怎么知道是哪个数据库。 //QT提供了这样一种构造函数 QSqlQuery(const QSqlDatabase &db) //指定数据库 //在QT6.2.4 MSVC2019调试通过。 //效果见下图&am…

uniapp对uni.request()的封装以及使用

官方文档 uni.request(OBJECT) | uni-app官网 (dcloud.net.cn) uni.request参数 参数名说明url是写api地址的data是用来传值的对于 GET 方法&#xff0c;会将数据 转换为 query string。例如 { name: name, age: 18 } 转换后的结果是 namename&age18。对于 POST 方法且 …

项目管理中常用的三个工具:甘特图、看板、燃尽图

在日常项目管理的实践中&#xff0c;为了更有效地追踪项目进度、优化资源配置和提高团队协作效率&#xff0c;管理者常常会借助一些工具来辅助工作。这些工具的本质在于将抽象复杂的项目管理任务具象化、简单化&#xff0c;以更直观、方便的方式呈现出来。 以下介绍项目管理中…

【opencv 加速推理】如何安装 支持cuda的opencv 包 用于截帧加速

要在支持CUDA的系统上安装OpenCV&#xff0c;您可以使用pip来安装支持CUDA的OpenCV版本。OpenCV支持CUDA加速&#xff0c;但需要安装额外的库&#xff0c;如cuDNN和NVIDIA CUDA Toolkit。以下是一般步骤&#xff1a; 安装NVIDIA CUDA Toolkit: 首先&#xff0c;您需要安装NVID…

【学习笔记】Python 使用 matplotlib 画图

文章目录 安装中文显示折线图、点线图柱状图、堆积柱状图坐标轴断点参考资料 本文将介绍如何使用 Python 的 matplotlib 库画图&#xff0c;记录一些常用的画图 demo 代码 安装 # 建议先切换到虚拟环境中 pip install matplotlib中文显示 新版的 matplotlib 已经支持字体回退…

【Linux】常用命令

1. 切换命令: cd 语法&#xff1a; cd [相对路径或绝对路径] 使用小tips: 输入文件夹名称过程中可以使用Tab来自动不全。 演示效果&#xff1a; 使用了相对路径和绝对路径&#xff0c;可以看到它们的效果是一样的。 2. 创建目录&#xff1a;mkdir 语法&#xff1a; mkdir […

OpenHarmony音视频—opus

简介 Opus是一种用于在互联网上进行交互式语音和音频传输的编解码器。它可以从低比特率窄带语音扩展到非常高的高品质立体声音乐。 下载安装 直接在OpenHarmony-SIG仓中搜索opus并下载。 使用说明 以OpenHarmony 3.1 Beta的rk3568版本为例 将下载的opus库代码存在以下路径&a…

【JAVA】PO、VO、DAO、BO、DTO、POJO你分得清吗?

在Java开发中&#xff0c;PO、VO、DAO、BO、DTO、POJO这些词汇是比较常见的&#xff0c;每个术语都有其特定的含义和用途。下面是它们的具体区别&#xff1a; 名称简要概况用途和特定PO (Persistence Object) 持…

基于Python实现的推箱子小游戏

Python贪吃蛇小游戏实现: 推箱子曾经在我们的童年给我们带来了很多乐趣。推箱子这款游戏现在基本上没人玩了&#xff0c;甚至在新一代人的印象中都已毫无记忆了。。。但是&#xff0c;这款游戏可以在一定程度上锻炼自己的编程能力。 运行效果如图所示&#xff1a; 游戏关卡有点…

锂电池寿命预测 | Matlab基于GRU门控循环单元的锂电池寿命预测

目录 预测效果基本介绍程序设计参考资料 预测效果 基本介绍 锂电池寿命预测 | Matlab基于GRU门控循环单元的锂电池寿命预测 Matlab基于GRU的锂电池剩余寿命预测 基于GRU的锂电池剩余寿命预测&#xff08;单变量&#xff09; 运行环境Matlab2020及以上 锂电池的剩余寿命预测是…

【前后端】django前后端交互

提示&#xff1a;文章写完后&#xff0c;目录可以自动生成&#xff0c;如何生成可参考右边的帮助文档 文章目录 前言一、django是什么二、django前后端交互指引三、总结 前言 随着开发语言及人工智能工具的普及&#xff0c;使得越来越多的人会主动学习使用一些开发语言&#x…

面试:JVM垃圾回收

一、三种垃圾回收算法 1、标记清除&#xff08;已废弃&#xff09; 找到根对象&#xff08;局部变量正在引用的对象、静态变量正在引用的对象&#xff09;&#xff1b;沿着根对象的引用链&#xff0c;查看当前的对象是否被根对象所引用&#xff0c;若被引用&#xff0c;则加上…

rabbitmq 使用SAC队列实现顺序消息

rabbitmq 使用SAC队列实现顺序消息 前提 SAC: single active consumer, 是指如果有多个实例&#xff0c;只允许其中一个实例消费&#xff0c;其他实例为空闲 目的 实现消息顺序消费&#xff0c;操作&#xff1a; 创建4个SAC队列,消息的路由key 取队列个数模&#xff0c;这…

[Java EE] 多线程(五):单例模式与阻塞队列

1. 单例模式 单例模式是校招中最长考的设计模式之一,首先我们来谈一谈什么是设计模式: 设计模式就好像象棋中的棋谱一样,如果红方走了什么样的局势,黑方就有一定地固定地套路,来应对这样的局势,按照固定地套路来,可以保证在该局势下不会吃亏. 软件开发也是同样的道理,有很多…

BGP的基本配置

l 按照以下步骤配置BGP协议&#xff1a; 第1步&#xff1a;设备基本参数配置&#xff0c;AS内配置IGP确保内部网络连通性&#xff1b; l 配置IGP&#xff08;OSPF协议等&#xff09;路由解决peer对等体的源和目标IP之间连通性&#xff0c;确保peer之间TCP&#xff08;179&a…

【后端】python与django的开发环境搭建指南

安装Git 双击Git 客户端安装文件&#xff0c;在安装页面&#xff0c;单击“Next” 在安装路径选择页面&#xff0c;保持默认&#xff0c;单击“Next” 在功能组件选择页面&#xff0c;保持默认&#xff0c;单击“Next” 在开始菜单文件夹设置页面&#xff0c;保持默认&am…

好看到爆炸的弹窗公告源码

源码介绍 好看到爆炸的弹窗公告源码&#xff0c;源码由HTMLCSSJS组成&#xff0c;记事本打开源码文件可以进行内容文字之类的修改&#xff0c;双击html文件可以本地运行效果&#xff0c;也可以上传到服务器里面&#xff0c; 源码截图 源码下载 好看到爆炸的弹窗公告源码

【Elasticsearch】Elasticsearch 从入门到精通(二):基础使用

《Elasticsearch 从入门到精通》共包含以下 2 2 2 篇文章&#xff1a; Elasticsearch 从入门到精通&#xff08;一&#xff09;&#xff1a;基本介绍Elasticsearch 从入门到精通&#xff08;二&#xff09;&#xff1a;基础使用 &#x1f60a; 如果您觉得这篇文章有用 ✔️ 的…

SpringBoot+vue开发记录(二)

说明&#xff1a;本篇文章的主要内容为SpringBoot开发中后端的创建 项目创建: 1. 新建项目&#xff1a; 如下&#xff0c;这样简单创建就行了&#xff0c;JDK什么的就先17&#xff0c;当然1.8也是可以的&#xff0c;后面可以改。 这样就创建好了&#xff1a; 2. pom.xml…