毫无疑问,Quicksort被认为是本世纪最重要的算法之一,并且它是许多语言(包括Java中的Arrays.sort
)的事实上的系统排序。
那么,quicksort有何新功能?
嗯,除了我现在(在Java 7发行了2年之久)才想到, Arrays.sort
的Quicksort实现已被替换为Dual-Pivot QuickSort的变体代替了。 出于这个原因,这个线程真棒,而且乔恩·本特利和约书亚·布洛赫真的很谦虚。
接下来我要做什么?
就像其他所有人一样,我想实现它并针对约1000万个整数(随机和重复)进行一些基准测试。
奇怪的是,我发现以下结果:
随机数据:
- 快速排序基本:1222毫秒
- 快速排序3种方式:1295毫秒(严重!)
- 快速分类双枢轴:1066毫秒
重复数据:
- 快速排序基础:378毫秒
- 快速排序3种方式:15毫秒
- 快速分类双枢轴:6毫秒
愚蠢的问题1:
恐怕在三向分区的实现中缺少一些东西。 在针对随机输入(1000万个)数字的多次运行中,我可以看到单个枢轴始终表现更好(尽管对于1000万个数字,相差不到100毫秒)。
我知道直到现在,将3-way Quicksort设置为默认Quicksort的全部目的是它不会在重复键上提供0(n 2)性能-当我对重复输入运行它时,这一点非常明显。 但是,为了处理重复的数据,三路方式会付出一点代价吗? 还是我的实现不好?
愚蠢的问题2
我的Dual Pivot实现(下面的链接)不能很好地处理重复项。 它需要永远的甜蜜(0(n 2))才能执行。 有避免这种情况的好方法吗? 谈到Arrays.sort
实现,我发现在完成实际排序之前,就消除了升序和重复项。 因此,作为一个肮脏的解决方法,如果枢轴相等,则我快进lowerIndex,直到它与pivot2不同。 这是一个公平的实施吗?
else if (pivot1==pivot2){while (pivot1==pivot2 && lowIndex<highIndex){lowIndex++;pivot1=input[lowIndex];}}
而已。 那就是我所做的一切?
我总是发现算法的跟踪很有趣,但是由于Dual Pivot quicksort中的变量数量众多,我的眼睛在调试时发现它不知所措。 因此,我还继续创建了启用跟踪的实现(针对所有3种实现),以便可以看到变量指针当前所在的位置。
这些启用了跟踪的类仅覆盖指针在数组值下方的位置。 我希望您发现这些课程有用。
例如。 用于Dual Pivot迭代
整个项目(以及DSA的一些la脚实现)可在github上找到 。 仅在这里可以找到 quicksort类。
这是我对SinglePivot(Hoare),3路(Sedgewick)和新的Dual-Pivot(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;}}
3路
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);}}
翻译自: https://www.javacodegeeks.com/2013/06/quicksorting-3-way-and-dual-pivot.html