本周算法:快速排序—三路快排 vs 双基准
来源:互联网 发布:淘宝睡衣常用模特 编辑:程序博客网 时间:2024/05/17 02:35
http://www.importnew.com/8445.html
原文链接: dzone 翻译: ImportNew.com - 风恋星
译文链接: http://www.importnew.com/8445.html
[ 转载请保留原文出处、译者和译文链接。]
本文由 ImportNew - 风恋星 翻译自 dzone。如需转载本文,请先参见文章末尾处的转载要求。
ImportNew注:如果你也对Java技术翻译分享感兴趣,欢迎加入我们的 Java开发 小组。参与方式请查看小组简介。
快速排序被公认为是本世纪最重要的算法之一,这已经不是什么新闻了。对很多语言来说是实际系统排序,包括在Java中的Arrays.sort
。
那么快速排序有什么新进展呢?
好吧,就像我刚才提到的那样(Java 7发布两年后)快速排序实现的Arrays.sort
被双基准(dual-pivot)排序的一种变体取代了。这篇文章不仅展示了为什么这个变化如此优秀,而且让我们看到Jon Bentley和Joshua Bloch的谦逊。
我当时做了什么?
与所有人一样,我想实现这个算法并且对一千万个数值排序(随机数据和重复数据)。奇怪的是,我得到了下面的结果:
随机数据:
- 基本排序:1222ms。
- 三路(Three-way)快速排序:1295ms(我是认真的!)。
- 双基准快速排序:1066ms。
重复数据:
- 基本排序:378ms。
- 三路快速排序:15ms。
- 双基准快速排序:6ms。
愚蠢的问题1
我担心自己在实现三路快速排序的时候遗漏了什么。在多次执行随机输入一千万个数值后,可以看到单点排序始终运行更良好。尽管在执行一千万个数值的时候差距小于100ms。
我现在明白了,用三路快速排序作为默认排序工具的目的。因为在重复数值时,它的时间复杂度没有0(n2)。当我在输入重复值数据时,结果非常明显。但是真的为了处理重复数据的缘故,三路快速排序会受到性能损失吗?或者是我实现方式有问题?
愚蠢的问题2
我的双基准快速排序在实现重复数据的时候并没有处理好,它执行时耗费了0(n2)的时间复杂度。有什么好的办法可以避免吗?实现数组排序时我发现,在实际排序前升序序列和重复就已经能得到很好地消除。所以,作为一种应急的办法,如果定位的数字与比较的数字相等,则增长lowerIndex 去比较下一位数直到与pivot2不相等为止。这种实现会没有问题吗?
else
if
(pivot1==pivot2){
while
(pivot1==pivot2 && lowIndex<highIndex){
lowIndex++;
pivot1=input[lowIndex];
}
}
这就是所有内容吗?我究竟做了哪些?
我一直觉得算法跟踪很有趣,但是双基准快速排序中出现的变量个数让我眼花缭乱。所以,接下来我在(三种)实现中都加入了调试信息,这样就可以看出实际运行中不同。
这些可跟踪的类只负责追踪数组下方的指针。希望你能发现这些类是很有用的。
例如一个双基准迭代器:
你可以从哪里下载代码?
整个项目(连同一些蹩脚的DSA实现)的实现可以在GitHub上找到。快速排序类就可以在这里找到。
这是我的实现单基准(Hoare),三路快排(Sedgewick)和新双基准(Yaroslavskiy)。
单基准:
package
basics.sorting.quick;
import
static
basics.sorting.utils.SortUtils.exchange;
import
static
basics.sorting.utils.SortUtils.less;
import
basics.shuffle.KnuthShuffle;
public
class
QuickSortBasic {
public
void
sort (
int
[] input){
//KnuthShuffle.shuffle(input);
sort (input,
0
, input.length-
1
);
}
private
void
sort(
int
[] input,
int
lowIndex,
int
highIndex) {
if
(highIndex<=lowIndex){
return
;
}
int
partIndex=partition (input, lowIndex, highIndex);
sort (input, lowIndex, partIndex-
1
);
sort (input, partIndex+
1
, highIndex);
}
private
int
partition(
int
[] input,
int
lowIndex,
int
highIndex) {
int
i=lowIndex;
int
pivotIndex=lowIndex;
int
j=highIndex+
1
;
while
(
true
){
while
(less(input[++i], input[pivotIndex])){
if
(i==highIndex)
break
;
}
while
(less (input[pivotIndex], input[--j])){
if
(j==lowIndex)
break
;
}
if
(i>=j)
break
;
exchange(input, i, j);
}
exchange(input, pivotIndex, j);
return
j;
}
}
三基准
package
basics.sorting.quick;
import
static
basics.shuffle.KnuthShuffle.shuffle;
import
static
basics.sorting.utils.SortUtils.exchange;
import
static
basics.sorting.utils.SortUtils.less;
public
class
QuickSort3Way {
public
void
sort (
int
[] input){
//input=shuffle(input);
sort (input,
0
, input.length-
1
);
}
public
void
sort(
int
[] input,
int
lowIndex,
int
highIndex) {
if
(highIndex<=lowIndex)
return
;
int
lt=lowIndex;
int
gt=highIndex;
int
i=lowIndex+
1
;
int
pivotIndex=lowIndex;
int
pivotValue=input[pivotIndex];
while
(i<=gt){
if
(less(input[i],pivotValue)){
exchange(input, i++, lt++);
}
else
if
(less (pivotValue, input[i])){
exchange(input, i, gt--);
}
else
{
i++;
}
}
sort (input, lowIndex, lt-
1
);
sort (input, gt+
1
, highIndex);
}
}
双基准
package
basics.sorting.quick;
import
static
basics.shuffle.KnuthShuffle.shuffle;
import
static
basics.sorting.utils.SortUtils.exchange;
import
static
basics.sorting.utils.SortUtils.less;
public
class
QuickSortDualPivot {
public
void
sort (
int
[] input){
//input=shuffle(input);
sort (input,
0
, input.length-
1
);
}
private
void
sort(
int
[] input,
int
lowIndex,
int
highIndex) {
if
(highIndex<=lowIndex)
return
;
int
pivot1=input[lowIndex];
int
pivot2=input[highIndex];
if
(pivot1>pivot2){
exchange(input, lowIndex, highIndex);
pivot1=input[lowIndex];
pivot2=input[highIndex];
//sort(input, lowIndex, highIndex);
}
else
if
(pivot1==pivot2){
while
(pivot1==pivot2 && lowIndex<highIndex){
lowIndex++;
pivot1=input[lowIndex];
}
}
int
i=lowIndex+
1
;
int
lt=lowIndex+
1
;
int
gt=highIndex-
1
;
while
(i<=gt){
if
(less(input[i], pivot1)){
exchange(input, i++, lt++);
}
else
if
(less(pivot2, input[i])){
exchange(input, i, gt--);
}
else
{
i++;
}
}
exchange(input, lowIndex, --lt);
exchange(input, highIndex, ++gt);
sort(input, lowIndex, lt-
1
);
sort (input, lt+
1
, gt-
1
);
sort(input, gt+
1
, highIndex);
}
}
- 本周算法:快速排序—三路快排 vs 双基准
- 算法之道(二):快速排序—三路快排 vs 双基准
- 快速排序、三路快排、双基准
- 快速排序--基准定位,分而治之
- 快速排序随机初始基准数
- 快速排序(基准是中位数)
- 快速排序(以中间为基准)
- 排序算法—快速排序
- 排序算法—快速排序
- 第16周项目1-验证算法(4)快速排序 以第1个元素作为基准
- 第16周项目1-验证算法(4)快速排序 以中间位置的元素作为基准
- 本周算法:图的拓扑排序
- 快速排序第一个为基准c语言版
- 快速排序、基准元的选取及其优化
- 快速排序VS堆排序
- 【算法】——快速排序
- 算法——快速排序
- 算法——快速排序
- C# List<T>泛型的排序和删除
- android开源项目[tool篇]
- 《数字图像处理》学习笔记(四)--混合空间增强法(待修改)
- system-config-selinux工具安装
- JsonKit 在xcode5 上报错
- 本周算法:快速排序—三路快排 vs 双基准
- XULRunner 下载安装
- mysql:四舍五入函数
- Java数据结构总论
- 使用Sublime Text 2 编辑Markdown
- 详解android:scaleType属性
- OpenCv 基于摄像头的帧差法源码
- js typeof用法
- easyui 俩个错误 解决方案