java中quicksort的参数_Java中双基准快速排序方法(DualPivotQuicksort.sort())的具体实现...

在Java语言的Arrays类下提供了一系列排序(sort)方法,帮助使用者对各种不同数据类型的数组进行排序. 在1.7之后的版本中, Arrays.sort()方法在操作过程中实际调用的是DualPivotQuicksort类下的sort方法,DualPivotQuicksort和Arrays一样,都在java.util包下,按字面翻译过来,就是双(Dual)基准(Pivot)快速排序(Quicksort)算法.

双基准快速排序算法于2009年由Vladimir Yaroslavskiy提出,是对经典快速排序(Classic Quicksort)进行优化后的一个版本, Java自1.7开始,均使用此方法作为默认排序算法. 接下来,本文就将对此方法的具体实现过程进行简单的介绍.

在正式进入对DualPivotQuicksort的介绍之前,我们先来对经典快速排序的实现思路进行一下简单的了解:

经典快速排序在操作过程中首先会从数组中选取一个基准(Pivot),这个基准可以是数组中的任意一个元素;

随后,将这个数组所有其他元素和Pivot进行比较,比Pivot小的数放在左侧,大的数放在右侧;

如此,我们就在Pivot的左侧和右侧各得到了一个新的数组;

接下来我们再在这两个新的数组中各自选取新的Pivot,把小的放在左侧,大的放在右侧,循环往复,最终就会得到由小到大顺序排列的数组.

下图(via wiki)便是Quicksort的一个具体实现:

20180801000401952194.gif

在对Quicksort的基本思路有了一定了解之后,下一步我们就来看Java中DualPivotQuicksort.sort是如何实现的;

本方法在实际工作过程中, 并不是对任何传入的数组都直接进行快速排序, 而是会先对数组进行一系列测试, 然后根据数组的具体情况选择最适合的算法来进行排序,下面我们结合源码来看:

首先, 以一个int数组为例,

当一个数组int[] a被传入DualPivotQuicksort.sort()时,该方法还会要求其他一系列参数:

1 static void sort(int[] a, int left, int right, int[] work, int workBase, int workLen)

其中,int[] a是需被排序的int数组, left和right是该数组中需要被排序的部分的左右界限. 而后面的work, workBase和workLen三个参数其实并不会参与双基准快速排序, 而是当系统认为本数组更适合使用归并排序(merge sort)的时候, 供归并排序使用.

但是,在实际使用中,我们并不希望为了排序设置这么多的参数,因此:

Arrays.sort()在调用DualPivotQuicksort.sort()之前,对int数组的排序提供了两种参数列表:

public static void sort(int[] a)

直接对int[] a 进行排序,以及:

public static void sort(int[] a, int fromIndex, int toIndex)

对int[] a 中从fromIndex到toIndex(包头不包尾)之间的元素进行排序.

在这里,Arrays.sort()会自动将int[] work, int workBase, int workLen设置为null,0,0 省去了使用者的麻烦.

紧接着,DualPivotQuicksort.sort()会对传入的int数组进行检测, 具体流程如下:

20180801000402482434.png

这里先贴上整个方法的完整源码, 然后按上图中的流程逐步分析, 只想看DualPivotQuicksort的话可以直接跳到下面第7点:

335b83df261c422459d4afc29ba290e5.png

d1b641f023dd079c9e4a800b96607d9d.gif

1 /**

2 * Sorts the specified range of the array using the given3 * workspace array slice if possible for merging4 *5 *@parama the array to be sorted6 *@paramleft the index of the first element, inclusive, to be sorted7 *@paramright the index of the last element, inclusive, to be sorted8 *@paramwork a workspace array (slice)9 *@paramworkBase origin of usable space in work array10 *@paramworkLen usable size of work array11 */

12 static void sort(int[] a, int left, intright,13 int[] work, int workBase, intworkLen) {14 //Use Quicksort on small arrays

15 if (right - left

20 /*

21 * Index run[i] is the start of i-th run22 * (ascending or descending sequence).23 */

24 int[] run = new int[MAX_RUN_COUNT + 1];25 int count = 0; run[0] =left;26

27 //Check if the array is nearly sorted

28 for (int k = left; k < right; run[count] =k) {29 if (a[k] < a[k + 1]) { //ascending

30 while (++k <= right && a[k - 1] <=a[k]);31 } else if (a[k] > a[k + 1]) { //descending

32 while (++k <= right && a[k - 1] >=a[k]);33 for (int lo = run[count] - 1, hi = k; ++lo < --hi; ) {34 int t = a[lo]; a[lo] = a[hi]; a[hi] =t;35 }36 } else { //equal

37 for (int m = MAX_RUN_LENGTH; ++k <= right && a[k - 1] ==a[k]; ) {38 if (--m == 0) {39 sort(a, left, right, true);40 return;41 }42 }43 }44

45 /*

46 * The array is not highly structured,47 * use Quicksort instead of merge sort.48 */

49 if (++count ==MAX_RUN_COUNT) {50 sort(a, left, right, true);51 return;52 }53 }54

55 //Check special cases56 //Implementation note: variable "right" is increased by 1.

57 if (run[count] == right++) { //The last run contains one element

58 run[++count] =right;59 } else if (count == 1) { //The array is already sorted

60 return;61 }62

63 //Determine alternation base for merge

64 byte odd = 0;65 for (int n = 1; (n <<= 1) < count; odd ^= 1);66

67 //Use or create temporary array b for merging

68 int[] b; //temp array; alternates with a

69 int ao, bo; //array offsets from ‘left‘

70 int blen = right - left; //space needed for b

71 if (work == null || workLen < blen || workBase + blen >work.length) {72 work = new int[blen];73 workBase = 0;74 }75 if (odd == 0) {76 System.arraycopy(a, left, work, workBase, blen);77 b =a;78 bo = 0;79 a =work;80 ao = workBase -left;81 } else{82 b =work;83 ao = 0;84 bo = workBase -left;85 }86

87 //Merging

88 for (int last; count > 1; count =last) {89 for (int k = (last = 0) + 2; k <= count; k += 2) {90 int hi = run[k], mi = run[k - 1];91 for (int i = run[k - 2], p = i, q = mi; i < hi; ++i) {92 if (q >= hi || p < mi && a[p + ao] <= a[q +ao]) {93 b[i + bo] = a[p++ +ao];94 } else{95 b[i + bo] = a[q++ +ao];96 }97 }98 run[++last] =hi;99 }100 if ((count & 1) != 0) {101 for (int i = right, lo = run[count - 1]; --i >=lo;102 b[i + bo] = a[i +ao]103 );104 run[++last] =right;105 }106 int[] t = a; a = b; b =t;107 int o = ao; ao = bo; bo =o;108 }109 }110

111 /**

112 * Sorts the specified range of the array by Dual-Pivot Quicksort.113 *114 *@parama the array to be sorted115 *@paramleft the index of the first element, inclusive, to be sorted116 *@paramright the index of the last element, inclusive, to be sorted117 *@paramleftmost indicates if this part is the leftmost in the range118 */

119 private static void sort(int[] a, int left, int right, booleanleftmost) {120 int length = right - left + 1;121

122 //Use insertion sort on tiny arrays

123 if (length

126 * Traditional (without sentinel) insertion sort,127 * optimized for server VM, is used in case of128 * the leftmost part.129 */

130 for (int i = left, j = i; i < right; j = ++i) {131 int ai = a[i + 1];132 while (ai

142 * Skip the longest ascending sequence.143 */

144 do{145 if (left >=right) {146 return;147 }148 } while (a[++left] >= a[left - 1]);149

150 /*

151 * Every element from adjoining part plays the role152 * of sentinel, therefore this allows us to avoid the153 * left range check on each iteration. Moreover, we use154 * the more optimized algorithm, so called pair insertion155 * sort, which is faster (in the context of Quicksort)156 * than traditional implementation of insertion sort.157 */

158 for (int k = left; ++left <= right; k = ++left) {159 int a1 = a[k], a2 =a[left];160

161 if (a1

169 while (a2 < a[--k]) {170 a[k + 1] =a[k];171 }172 a[k + 1] =a2;173 }174 int last =a[right];175

176 while (last < a[--right]) {177 a[right + 1] =a[right];178 }179 a[right + 1] =last;180 }181 return;182 }183

184 //Inexpensive approximation of length / 7

185 int seventh = (length >> 3) + (length >> 6) + 1;186

187 /*

188 * Sort five evenly spaced elements around (and including) the189 * center element in the range. These elements will be used for190 * pivot selection as described below. The choice for spacing191 * these elements was empirically determined to work well on192 * a wide variety of inputs.193 */

194 int e3 = (left + right) >>> 1; //The midpoint

195 int e2 = e3 -seventh;196 int e1 = e2 -seventh;197 int e4 = e3 +seventh;198 int e5 = e4 +seventh;199

200 //Sort these elements using insertion sort

201 if (a[e2] < a[e1]) { int t = a[e2]; a[e2] = a[e1]; a[e1] =t; }202

203 if (a[e3] < a[e2]) { int t = a[e3]; a[e3] = a[e2]; a[e2] =t;204 if (t < a[e1]) { a[e2] = a[e1]; a[e1] =t; }205 }206 if (a[e4] < a[e3]) { int t = a[e4]; a[e4] = a[e3]; a[e3] =t;207 if (t < a[e2]) { a[e3] = a[e2]; a[e2] =t;208 if (t < a[e1]) { a[e2] = a[e1]; a[e1] =t; }209 }210 }211 if (a[e5] < a[e4]) { int t = a[e5]; a[e5] = a[e4]; a[e4] =t;212 if (t < a[e3]) { a[e4] = a[e3]; a[e3] =t;213 if (t < a[e2]) { a[e3] = a[e2]; a[e2] =t;214 if (t < a[e1]) { a[e2] = a[e1]; a[e1] =t; }215 }216 }217 }218

219 //Pointers

220 int less = left; //The index of the first element of center part

221 int great = right; //The index before the first element of right part

222

223 if (a[e1] != a[e2] && a[e2] != a[e3] && a[e3] != a[e4] && a[e4] !=a[e5]) {224 /*

225 * Use the second and fourth of the five sorted elements as pivots.226 * These values are inexpensive approximations of the first and227 * second terciles of the array. Note that pivot1 <= pivot2.228 */

229 int pivot1 =a[e2];230 int pivot2 =a[e4];231

232 /*

233 * The first and the last elements to be sorted are moved to the234 * locations formerly occupied by the pivots. When partitioning235 * is complete, the pivots are swapped back into their final236 * positions, and excluded from subsequent sorting.237 */

238 a[e2] =a[left];239 a[e4] =a[right];240

241 /*

242 * Skip elements, which are less or greater than pivot values.243 */

244 while (a[++less] pivot2);246

247 /*

248 * Partitioning:249 *250 * left part center part right part251 * +--------------------------------------------------------------+252 * | < pivot1 | pivot1 <= && <= pivot2 | ? | > pivot2 |253 * +--------------------------------------------------------------+254 * ^ ^ ^255 * | | |256 * less k great257 *258 * Invariants:259 *260 * all in (left, less) < pivot1261 * pivot1 <= all in [less, k) <= pivot2262 * all in (great, right) > pivot2263 *264 * Pointer k is the first index of ?-part.265 */

266 outer:267 for (int k = less - 1; ++k <=great; ) {268 int ak =a[k];269 if (ak < pivot1) { //Move a[k] to left part

270 a[k] =a[less];271 /*

272 * Here and below we use "a[i] = b; i++;" instead273 * of "a[i++] = b;" due to performance issue.274 */

275 a[less] =ak;276 ++less;277 } else if (ak > pivot2) { //Move a[k] to right part

278 while (a[great] >pivot2) {279 if (great-- ==k) {280 breakouter;281 }282 }283 if (a[great] < pivot1) { //a[great] <= pivot2

284 a[k] =a[less];285 a[less] =a[great];286 ++less;287 } else { //pivot1 <= a[great] <= pivot2

288 a[k] =a[great];289 }290 /*

291 * Here and below we use "a[i] = b; i--;" instead292 * of "a[i--] = b;" due to performance issue.293 */

294 a[great] =ak;295 --great;296 }297 }298

299 //Swap pivots into their final positions

300 a[left] = a[less - 1]; a[less - 1] =pivot1;301 a[right] = a[great + 1]; a[great + 1] =pivot2;302

303 //Sort left and right parts recursively, excluding known pivots

304 sort(a, left, less - 2, leftmost);305 sort(a, great + 2, right, false);306

307 /*

308 * If center part is too large (comprises > 4/7 of the array),309 * swap internal pivot values to ends.310 */

311 if (less < e1 && e5

313 * Skip elements, which are equal to pivot values.314 */

315 while (a[less] ==pivot1) {316 ++less;317 }318

319 while (a[great] ==pivot2) {320 --great;321 }322

323 /*

324 * Partitioning:325 *326 * left part center part right part327 * +----------------------------------------------------------+328 * | == pivot1 | pivot1 < && < pivot2 | ? | == pivot2 |329 * +----------------------------------------------------------+330 * ^ ^ ^331 * | | |332 * less k great333 *334 * Invariants:335 *336 * all in (*, less) == pivot1337 * pivot1 < all in [less, k) < pivot2338 * all in (great, *) == pivot2339 *340 * Pointer k is the first index of ?-part.341 */

342 outer:343 for (int k = less - 1; ++k <=great; ) {344 int ak =a[k];345 if (ak == pivot1) { //Move a[k] to left part

346 a[k] =a[less];347 a[less] =ak;348 ++less;349 } else if (ak == pivot2) { //Move a[k] to right part

350 while (a[great] ==pivot2) {351 if (great-- ==k) {352 breakouter;353 }354 }355 if (a[great] == pivot1) { //a[great] < pivot2

356 a[k] =a[less];357 /*

358 * Even though a[great] equals to pivot1, the359 * assignment a[less] = pivot1 may be incorrect,360 * if a[great] and pivot1 are floating-point zeros361 * of different signs. Therefore in float and362 * double sorting methods we have to use more363 * accurate assignment a[less] = a[great].364 */

365 a[less] =pivot1;366 ++less;367 } else { //pivot1 < a[great] < pivot2

368 a[k] =a[great];369 }370 a[great] =ak;371 --great;372 }373 }374 }375

376 //Sort center part recursively

377 sort(a, less, great, false);378

379 } else { //Partitioning with one pivot

380 /*

381 * Use the third of the five sorted elements as pivot.382 * This value is inexpensive approximation of the median.383 */

384 int pivot =a[e3];385

386 /*

387 * Partitioning degenerates to the traditional 3-way388 * (or "Dutch National Flag") schema:389 *390 * left part center part right part391 * +-------------------------------------------------+392 * | < pivot | == pivot | ? | > pivot |393 * +-------------------------------------------------+394 * ^ ^ ^395 * | | |396 * less k great397 *398 * Invariants:399 *400 * all in (left, less) < pivot401 * all in [less, k) == pivot402 * all in (great, right) > pivot403 *404 * Pointer k is the first index of ?-part.405 */

406 for (int k = less; k <= great; ++k) {407 if (a[k] ==pivot) {408 continue;409 }410 int ak =a[k];411 if (ak < pivot) { //Move a[k] to left part

412 a[k] =a[less];413 a[less] =ak;414 ++less;415 } else { //a[k] > pivot - Move a[k] to right part

416 while (a[great] >pivot) {417 --great;418 }419 if (a[great] < pivot) { //a[great] <= pivot

420 a[k] =a[less];421 a[less] =a[great];422 ++less;423 } else { //a[great] == pivot

424 /*

425 * Even though a[great] equals to pivot, the426 * assignment a[k] = pivot may be incorrect,427 * if a[great] and pivot are floating-point428 * zeros of different signs. Therefore in float429 * and double sorting methods we have to use430 * more accurate assignment a[k] = a[great].431 */

432 a[k] =pivot;433 }434 a[great] =ak;435 --great;436 }437 }438

439 /*

440 * Sort left and right parts recursively.441 * All elements from center part are equal442 * and, therefore, already sorted.443 */

444 sort(a, left, less - 1, leftmost);445 sort(a, great + 1, right, false);446 }447 }

DualPivotQuickSort.sort()

1. 判断数组int[] a的长度是否大于常量QUICKSORT_THRESHOLD, 即286:

286是java设定的一个阈值,当数组长度小于此值时, 系统将不再考虑merge sort, 直接将参数传入本类中的另一个私有sort方法进行排序,

private static void sort(long[] a, int left, int right, boolean leftmost)

335b83df261c422459d4afc29ba290e5.png

d1b641f023dd079c9e4a800b96607d9d.gif

1 //Use Quicksort on small arrays

2 if (right - left

判断数组int[] a的长度是否大于常量QUICKSORT_THRESHOLD

2. 继续判断int[] a的长度是否大于常量INSERTION_SORT_THRESHOLD, 即47:

3. 若数组长度小于47, 则使用insertion sort:

数组传入本类私有的sort方法后, 会继续判断数组长度是否大于47, 若小于此值, 则直接使用insertion sort并返回结果, 因为插入算法并非本文重点, 此处不再展开叙述,

335b83df261c422459d4afc29ba290e5.png

d1b641f023dd079c9e4a800b96607d9d.gif

1 int length = right - left + 1;2

3 //Use insertion sort on tiny arrays

4 if (length

7 * Traditional (without sentinel) insertion sort,8 * optimized for server VM, is used in case of9 * the leftmost part.10 */

11 for (int i = left, j = i; i < right; j = ++i) {12 int ai = a[i + 1];13 while (ai

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值