双轴快速排序(Dual Pivot Quicksort)
双轴快速排序(Dual Pivot Quicksort)
引子
在Timsort的分析前,我提到了在使用Arrays.sort()时针对int[]会使用DualPivotQuicksort类进行排序。实际上针对非对象类型,都会使用这个方法。JDK1.7开始使用双轴快速排序进行排序。
双轴快速排序的特点主要有:
- 比传统快排更快。
- 分治思想。“divide and conquer”
- 使用两个轴pivot而不是一个。
下文会具体介绍。
另外值得一提的是,JDK 1.8中DualPivotQuicksort并没有完全使用双轴快速排序的做法,而是按照基本数据类型和数组长度,用了快速排序、计数排序、归并排序:)最后我会分析源码加以说明。
一般快速排序的思想
对于排序算法来说,最耗时的部分一般来说为两种:比较和交换。当然这也是排序算法的核心步骤。
We compare sorting methods by the number of the most “expensive” operations, which influence on effectiveness of the sorting techniques, — comparisons and swaps. Quicksort algorithm is an effective and wide-spread sorting procedure with Cnln(n) operations, where n is the size of the arranged array. The problem is to find an algorithm with the least coefficient C.
参考Dual Pivot快排原始论文,研究新的快速排序方法,目的在于降低复杂度中的常数C。
一般的快速排序步骤如下:
- 从要排序的列表中选择一个元素,称作轴(这个翻译有点神奇)pivot。
- 整理列表,使得轴左侧的元素均小于pivot,轴右侧的元素均大于pivot。(其中等于的放在左右侧均可)这个过程称为partition,结束后轴元素便确定了其最终的位置。
- 递归的使用1、2的步骤来排序轴左侧和右侧的两个子序列。
一开始针对这个算法的优化大多有两种,一种是优化“divide and conquer”算法的实现,另一种是试图通过选取特定的轴pivot元素,来改善性能。但这里他们都是基于二分区,也就是一个轴的方式。
Vladimir Yaroslavskiy等人的研究提出了一个双轴的方式(two pivot,or partitioning to three parts),并证明了针对长度较大的序列是一种更有效的方式。
双轴快速排序步骤
首先定义几个变量。我们要排序的原始序列T[] a,T代表一个原始类型(int,float,byte,char,double,long,short),也就是非对象并且可比较的类型。
P1、P2为选择的两个pivot轴,其指针位置用left和right变量表示。
我们再定义三个指针L、K和**G,**其中位置见下图。三个指针区分出了四个分区。
这里我直接使用原始论文的图片了哈,画的很清楚了:)原始论文
双轴快速排序的分区
上图表示在算法过程中的某一个时刻的部分。算法步骤如下:
- 对于小序列,长度小于27的序列,使用插入排序。(后文JDK 1.8中使用的是47)
- 选择两个轴元素P1、P2。例如图中选择第一个元素a[left] = P1,最后一个元素a[right] = P2。
- 我们限定P1 < P2(如果不是,则交换P1、P2)。这样我们就可以分出如下部分:Part I [left, L - 1]为< P1部分,Part II [L, K - 1]为 >= P1且<= P2部分,Part III [G + 1, right - 1]为> P2部分,Part IV [K, G]为还没有确定的部分。
- 针对当前Part IV中的a[K],与P1和P2比较,比较后放入Part I II III中的一个。
- 调整L、K、G到适合的位置。
- 重复4、5步直到K > G。也就是说PartIV的元素全部分散到Part I II III中,最后是三个Part。
- 将P1放入PartI的最后一个位置,P2放入PartIII的前一个位置(或者第一个位置)。这样就确定了P1和P2的位置。
- 对于PartI II III ,重复1-7步。
性能与优势
经过证明,比较的平均次数为2nln(n)次,平均交换次数为0.8nln(n)。比普通快排要好。
It is proved that for the Dual-Pivot Quicksort the average number of comparisons is 2*nln(n), the average number of swaps is 0.8*nln(n), whereas classical Quicksort algorithm has 2*nln(n)* and 1*nln(n)* respectively.
对比JDK6.0的快排:
实验表明在随机分布的序列中,需要排序的序列长度越大,双轴快排的性能领先越明显。
双轴快排在非随机序列和有重复元素的序列中表现也不错。
在选择P1、P2双轴时,不使用第一个位置和最后一个位置,而是使用中间的两个元素,例如:
int third = arrayLength / 3;
P1 = a[left + third];
P2 = a[right - third];
这样的性能也会有额外的提升,并且在随机排列的序列中表现也没有下降。
JDK 1.8 DualPivotQuicksort源码分析
点开源码,我们发现这个类中有几个静态变量:
Tuning Parameters
看一下英文说明不难发现,这里的意思是根据array的不同长度,选择不同的排序方法。这里的run和Timsort中run是一个含义,代表升序或降序的序列(此处建议先理解一下什么是Timsort中的Run)。
这里一个有趣的事情是,JDK针对七种数据类型实现了七个方法。(当然这里主要是因为方法参数是不同的,没法写到一起),当然有一些细节还是有区别的。比如我们知道,char的范围是很小的,当array长度很大时,这种分布方式恰好使用计数排序是更快的。
当然这里不失一般性,我们还是使用int整数类型来理解排序过程,其他类型感兴趣的读者,自己看源码吧:)。int类型的sort步骤如下:
- 长度小于QUICKSORT_THRESHOLD,使用快排。
- 判断run个数,当run个数满足大于MAX_RUN_COUNT时,或相同的相连元素个数大于MAX_RUN_LENGTH时,使用快排。(说明数据分布偏随机化,或相等的相连元素比较多。)
- 根据2中已经分好的run,使用归并排序。
快排的步骤如下:
首先我们知道快排方法实现时是一个递归调用的过程,在下文中由于有条件判断,“走快排”或“走双轴快排”,我是特指当前调用过程中,分part的过程,分完part之后针对每个part会重新调用一次快排,此时还需要重新判断,而不是就“永远走双轴快排”了。因为每一个part条件会变。
- 长度n小于INSERTION_SORT_THRESHOLD,走插入排序。
- 取 m = n / 7,找到序列中间数e3,向左偏移和向右偏移m个分别两次,也就是一共取五个位置数e1,e2,e3,e4,e5。使用插入排序将这五个位置数排序。
- 如果2中五个数排序后相邻的两两不相同a[e1] != a[e2] && a[e2] != a[e3] && a[e3] != a[e4] && a[e4] != a[e5],则取a[e2]和a[e4]作为P1和P2,走双轴快排。否则取pivot = a[e3],走普通的快排。
值得一提的是,这里的普通快排也是走三向的,中间部分是等于。看下文注释截图就好了。
双轴快排和上文描述基本一致。
普通快排中间part是==pivot部分。
双轴快速排序(Dual Pivot Quicksort)相关推荐
- 单轴快排(SinglePivotQuickSort)和双轴快排(DualPivotQuickSort)及其JAVA实现
快速排序使用的是分治思想,将原问题分成若干个子问题进行递归解决.通过一趟排序将要排序的数据分割成独立的两部分,其中一部分的所有数据都比另外一部分的所有数据都要小,然后再按此方法对这两部分数据分别进行快 ...
- 浅谈Java底层排序双轴快排
在Java 底层的排序中有几个非常有用并且面试可以装逼的算法,TimSort,DualPivotQuicksort( 双轴快速排序 ),( binarySort)二分插入排序. 这三个排序,TimSo ...
- 双枢轴快速排序与 Arrays.sort()
时间复杂度差距 当数据量大起来的时候可以看到n*log(n)以及n2,n3,2^n的时间复杂度图像都已经快成竖线了.所以在大数据面前你还在写两个for的n平方时间复杂度的暴力解法算法吗? Arrays ...
- 【排序算法】——图解双轴快排(建议收藏)
原创公众号:「bigsai」,转载需注明出处 关注回复bigsai领取Java进阶pdf,回复进群加入力扣打卡群(目前200+). 觉得不错还请一键三连! 前言 在排序算法中,快排是占比非常多的一环, ...
- 排序算法--快速排序(QuickSort)、 3区快速排序(3 Way QuickSort)原理、适用场景及代码示例
快速排序 概念介绍 QuickSort快速和归并排序一样,是采用分治法解决问题的一个典型应用.它选择一个元素作为基准元素,并围绕选定的基准元素对给定数组进行分区. quickSort有很多不同的版本, ...
- Java 细节汇总(4)-Arrays 中的双轴快排
文章目录 1. Arrays 中的双轴快排 2. Java 中 switch 支持字符串的原理 3. Java 中 break,continue 标签的用法 4. Java 中 Math.ceil() ...
- 舵机任意角度程序_真香!!!飞特发布性价比超高的19kg磁编码360°双轴串口总线舵机STS3215...
2020年4月6日,深圳飞特模型有限公司发布了2020年新款磁编码版本的TTL串口总线舵机.这款舵机是基于SCS215电位器版本开发的更高性能的磁编码版本,不仅具备了飞特SM高端系列的高性价比功能,又 ...
- 双轴机械臂串口控制命令开发与测试:STM32F103控制板,简易调试命令集合
▌01 底层串口控制命令 1.调试说明 本文是继 调试机械臂一体化控制电路 博文中对于 两轴机械臂+机械爪整体控制板设计与机械爪控制调试 在 基于STM32F103双轴机械臂完整电路板 控制下进行串口 ...
- 组装肩部带有减速器双轴机械臂组装与调试
➤ 00背景 在 增加了机械爪的双轴机械臂 安装调试之后, 发现进行平顺控制效果不好 ,因此在原来的基础上进行了如下的改动: 肩部和肘部的角度传感器采用了: 角度编码器 ST-3806-15-RS 读 ...
最新文章
- CVPR2019论文看点:自学习Anchor原理
- mysql中 课程1比课程2成绩高_小菜菜mysql练习解读分析1——查询 01 课程比 02 课程成绩高的学生的信息及课程分数......
- WPF 与Surface 2.0 SDK 亲密接触 - ScatterView 数据绑定篇
- 10停止nginx命令 win_Linux下配置Nginx并使用https协议
- 使用rsync工具构建php项目管理平台
- java游戏细菌_细菌Bacteria
- 做业务千万不要把鸡蛋放在一个篮子里
- 我儿子竟跟男孩子抱在一起
- [Usaco2007 Demo]City skyline
- DirectX12(D3D12)基础教程(十八)—— PBR基础从物理到艺术(中)
- Flash版Logo语言9.83
- Netty 解决TCP粘包/半包使用
- php api 实例maccms,api.php · do do/maccms10 - Gitee.com
- java 大小写转换函数_java字符串大小写转换的两种方法
- 大数据技术之HFDS
- java 输出乘法口诀第一列_java输出乘法口诀表
- 测试用例的设计方法及案例
- c++如何批量修改文件后缀名
- 农历类==解析指定的日期 1900-2100
- 不要再走弯路了,黑客学习路线看这里
热门文章
- 不朽凡人 正文 第四百五十三章 天仙领域
- html修改div里的图片,html 如何将div 里面的图片设置与div等宽,没有缝隙?
- 妙笔生画:用desmos在线绘制y=f(x)或z=f(x,y)数学函数图像
- qt实现的五子棋小游戏(Qpainter)
- linux testdisk使用教程,江湖救急!磁盘数据大救星TestDisk
- 推荐14款最受欢迎的3d建模软件
- Charls抓包工具使用
- Charls免费注册
- 40年的努力,人类基因组测序终于完成
- 利用QR算法求解矩阵的特征值和特征向量