DualPivotQuicksort中文名称:双支点快速排序。
DualPivotQuicksort是JDK1.7开始的采用的快速排序算法。
一般的快速排序采用一个枢轴来把一个数组划分成两半,然后递归之。
大量经验数据表面,采用两个枢轴来划分成3份的算法更高效,这就是DualPivotQuicksort。
直接调用的sort第一级:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
|
public
static
void
sort(
int
[] a,
int
left,
int
right) {
// Use Quicksort on small arrays
if
(right - left < QUICKSORT_THRESHOLD) {
//门限为286
sort(a, left, right,
true
);
return
;
}
/*
* Index run[i] is the start of i-th run
* (ascending or descending sequence).
*/
int
[] run =
new
int
[MAX_RUN_COUNT +
1
];
int
count =
0
; run[
0
] = left;
// Check if the array is nearly sorted
for
(
int
k = left; k < right; run[count] = k) {
if
(a[k] < a[k +
1
]) {
// ascending
while
(++k <= right && a[k -
1
] <= a[k]);
}
else
if
(a[k] > a[k +
1
]) {
// descending
while
(++k <= right && a[k -
1
] >= a[k]);
for
(
int
lo = run[count] -
1
, hi = k; ++lo < --hi; ) {
int
t = a[lo]; a[lo] = a[hi]; a[hi] = t;
}
}
else
{
// equal
for
(
int
m = MAX_RUN_LENGTH; ++k <= right && a[k -
1
] == a[k]; ) {
if
(--m ==
0
) {
sort(a, left, right,
true
);
return
;
}
}
}
/*
* The array is not highly structured,
* use Quicksort instead of merge sort.
*/
if
(++count == MAX_RUN_COUNT) {
sort(a, left, right,
true
);
return
;
}
}
// Check special cases
if
(run[count] == right++) {
// The last run contains one element
run[++count] = right;
}
else
if
(count ==
1
) {
// The array is already sorted
return
;
}
/*
* Create temporary array, which is used for merging.
* Implementation note: variable "right" is increased by 1.
*/
int
[] b;
byte
odd =
0
;
for
(
int
n =
1
; (n <<=
1
) < count; odd ^=
1
);
if
(odd ==
0
) {
b = a; a =
new
int
[b.length];
for
(
int
i = left -
1
; ++i < right; a[i] = b[i]);
}
else
{
b =
new
int
[a.length];
}
// Merging
for
(
int
last; count >
1
; count = last) {
for
(
int
k = (last =
0
) +
2
; k <= count; k +=
2
) {
int
hi = run[k], mi = run[k -
1
];
for
(
int
i = run[k -
2
], p = i, q = mi; i < hi; ++i) {
if
(q >= hi || p < mi && a[p] <= a[q]) {
b[i] = a[p++];
}
else
{
b[i] = a[q++];
}
}
run[++last] = hi;
}
if
((count &
1
) !=
0
) {
for
(
int
i = right, lo = run[count -
1
]; --i >= lo;
b[i] = a[i]
);
run[++last] = right;
}
int
[] t = a; a = b; b = t;
}
}
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
|
/**
* Sorts the specified range of the array by Dual-Pivot Quicksort.
*
* @param a the array to be sorted
* @param left the index of the first element, inclusive, to be sorted
* @param right the index of the last element, inclusive, to be sorted
* @param leftmost indicates if this part is the leftmost in the range
*/
private
static
void
sort(
int
[] a,
int
left,
int
right,
boolean
leftmost) {
int
length = right - left +
1
;
// Use insertion sort on tiny arrays
if
(length < INSERTION_SORT_THRESHOLD) {
//47个
if
(leftmost) {
/*
* Traditional (without sentinel) insertion sort,
* optimized for server VM, is used in case of
* the leftmost part.
*/
for
(
int
i = left, j = i; i < right; j = ++i) {
int
ai = a[i +
1
];
while
(ai < a[j]) {
a[j +
1
] = a[j];
if
(j-- == left) {
break
;
}
}
a[j +
1
] = ai;
}
}
else
{
/*
* Skip the longest ascending sequence.
*/
do
{
if
(left >= right) {
return
;
}
}
while
(a[++left] >= a[left -
1
]);
/*
* Every element from adjoining part plays the role
* of sentinel, therefore this allows us to avoid the
* left range check on each iteration. Moreover, we use
* the more optimized algorithm, so called pair insertion
* sort, which is faster (in the context of Quicksort)
* than traditional implementation of insertion sort.
*/
for
(
int
k = left; ++left <= right; k = ++left) {
int
a1 = a[k], a2 = a[left];
if
(a1 < a2) {
a2 = a1; a1 = a[left];
}
while
(a1 < a[--k]) {
a[k +
2
] = a[k];
}
a[++k +
1
] = a1;
while
(a2 < a[--k]) {
a[k +
1
] = a[k];
}
a[k +
1
] = a2;
}
int
last = a[right];
while
(last < a[--right]) {
a[right +
1
] = a[right];
}
a[right +
1
] = last;
}
return
;
}
|
当小于47个时,使用插入排序。
参数a为需要排序的数组,left代表需要排序的数组区间中最左边元素的索引,right代表区间中最右边元素的索引,leftmost代表该区间是否是数组中最左边的区间。举个例子:
数组:[2, 4, 8, 5, 6, 3, 0, -3, 9]可以分成三个区间(2, 4, 8){5, 6}<3, 0, -3, 9>
对于()区间,left=0, right=2, leftmost=true
对于 {}区间, left=3, right=4, leftmost=false,同理可得<>区间的相应参数
当区间长度小于47时,该方法会采用插入排序;否则采用快速排序。
1、 当leftmost为true时,它会采用传统的插入排序(traditional insertion sort),代码也较简单,其过程类似打牌时抓牌插牌。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
|
// Inexpensive approximation of length / 7,1/7=1/8+1/32
int
seventh = (length >>
3
) + (length >>
6
) +
1
;
/*
* Sort five evenly spaced elements around (and including) the
* center element in the range. These elements will be used for
* pivot selection as described below. The choice for spacing
* these elements was empirically determined to work well on
* a wide variety of inputs.
*/
int
e3 = (left + right) >>>
1
;
// The midpoint
int
e2 = e3 - seventh;
int
e1 = e2 - seventh;
int
e4 = e3 + seventh;
int
e5 = e4 + seventh;
// Sort these elements using insertion sort
if
(a[e2] < a[e1]) {
int
t = a[e2]; a[e2] = a[e1]; a[e1] = t; }
if
(a[e3] < a[e2]) {
int
t = a[e3]; a[e3] = a[e2]; a[e2] = t;
if
(t < a[e1]) { a[e2] = a[e1]; a[e1] = t; }
}
if
(a[e4] < a[e3]) {
int
t = a[e4]; a[e4] = a[e3]; a[e3] = t;
if
(t < a[e2]) { a[e3] = a[e2]; a[e2] = t;
if
(t < a[e1]) { a[e2] = a[e1]; a[e1] = t; }
}
}
if
(a[e5] < a[e4]) {
int
t = a[e5]; a[e5] = a[e4]; a[e4] = t;
if
(t < a[e3]) { a[e4] = a[e3]; a[e3] = t;
if
(t < a[e2]) { a[e3] = a[e2]; a[e2] = t;
if
(t < a[e1]) { a[e2] = a[e1]; a[e1] = t; }
}
}
}
// Pointers
int
less = left;
// The index of the first element of center part
int
great = right;
// The index before the first element of right part
if
(a[e1] != a[e2] && a[e2] != a[e3] && a[e3] != a[e4] && a[e4] != a[e5]) {
/*
* Use the second and fourth of the five sorted elements as pivots.
* These values are inexpensive approximations of the first and
* second terciles of the array. Note that pivot1 <= pivot2.
*/
int
pivot1 = a[e2];
int
pivot2 = a[e4];
/*
* The first and the last elements to be sorted are moved to the
* locations formerly occupied by the pivots. When partitioning
* is complete, the pivots are swapped back into their final
* positions, and excluded from subsequent sorting.
*/
a[e2] = a[left];
a[e4] = a[right];
|
1. pivot的选取方式是将数组分成近视等长的七段,而这七段其实是被5个元素分开的,将这5个元素从小到大排序,取出第2个和第4个,分别作为pivot1和pivot2。
2. Pivot选取完之后,分别从左右两端向中间遍历,左边遍历停止的条件是遇到一个大于等于pivot1的值,并把那个位置标记为less;右边遍历的停止条件是遇到一个小于等于pivot2的值,并把那个位置标记为great
3. 然后从less位置向后遍历,遍历的位置用k表示,会遇到以下几种情况:
a. k位置的值比pivot1小,那就交换k位置和less位置的值,并是less的值加1;这样就使得less位置左边的值都小于pivot1,而less位置和k位置之间的值大于等于pivot1
b. k位置的值大于pivot2,那就从great位置向左遍历,遍历停止条件是遇到一个小于等于pivot2的值,假如这个值小于pivot1,就把这个值 写到less位置,把less位置的值写道k位置,把k位置的值写道great位置,最后less++,great--;加入这个值大于等于 pivot1,就交换k位置和great位置,之后great--。
4. 完成上述过程之后,带排序的子区间就被分成了三段(pivot2),最后分别对这三段采用递归就行了。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
|
/*
* Skip elements, which are less or greater than pivot values.
*/
while
(a[++less] < pivot1);
while
(a[--great] > pivot2);
/*
* Partitioning:
*
* left part center part right part
* +--------------------------------------------------------------+
* | < pivot1 | pivot1 <= && <= pivot2 | ? | > pivot2 |
* +--------------------------------------------------------------+
* ^ ^ ^
* | | |
* less k great
*
* Invariants:
*
* all in (left, less) < pivot1
* pivot1 <= all in [less, k) <= pivot2
* all in (great, right) > pivot2
*
* Pointer k is the first index of ?-part.
*/
outer:
for
(
int
k = less -
1
; ++k <= great; ) {
int
ak = a[k];
if
(ak < pivot1) {
// Move a[k] to left part
a[k] = a[less];
/*
* Here and below we use "a[i] = b; i++;" instead
* of "a[i++] = b;" due to performance issue.
*/
a[less] = ak;
++less;
}
else
if
(ak > pivot2) {
// Move a[k] to right part
while
(a[great] > pivot2) {
if
(great-- == k) {
break
outer;
}
}
if
(a[great] < pivot1) {
// a[great] <= pivot2
a[k] = a[less];
a[less] = a[great];
++less;
}
else
{
// pivot1 <= a[great] <= pivot2
a[k] = a[great];
}
/*
* Here and below we use "a[i] = b; i--;" instead
* of "a[i--] = b;" due to performance issue.
*/
a[great] = ak;
--great;
}
}
// Swap pivots into their final positions
a[left] = a[less -
1
]; a[less -
1
] = pivot1;
a[right] = a[great +
1
]; a[great +
1
] = pivot2;
// Sort left and right parts recursively, excluding known pivots
sort(a, left, less -
2
, leftmost);
sort(a, great +
2
, right,
false
);
|
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
|
/*
* If center part is too large (comprises > 4/7 of the array),
* swap internal pivot values to ends.
*/
if
(less < e1 && e5 < great) {
/*
* Skip elements, which are equal to pivot values.
*/
while
(a[less] == pivot1) {
++less;
}
while
(a[great] == pivot2) {
--great;
}
/*
* Partitioning:
*
* left part center part right part
* +----------------------------------------------------------+
* | == pivot1 | pivot1 < && < pivot2 | ? | == pivot2 |
* +----------------------------------------------------------+
* ^ ^ ^
* | | |
* less k great
*
* Invariants:
*
* all in (*, less) == pivot1
* pivot1 < all in [less, k) < pivot2
* all in (great, *) == pivot2
*
* Pointer k is the first index of ?-part.
*/
outer:
for
(
int
k = less -
1
; ++k <= great; ) {
int
ak = a[k];
if
(ak == pivot1) {
// Move a[k] to left part
a[k] = a[less];
a[less] = ak;
++less;
&nbs
|