OLD | NEW |
| (Empty) |
1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file | |
2 // for details. All rights reserved. Use of this source code is governed by a | |
3 // BSD-style license that can be found in the LICENSE file. | |
4 | |
5 part of dart.collection.dev; | |
6 | |
7 /** | |
8 * Dual-Pivot Quicksort algorithm. | |
9 * | |
10 * This class implements the dual-pivot quicksort algorithm as presented in | |
11 * Vladimir Yaroslavskiy's paper. | |
12 * | |
13 * Some improvements have been copied from Android's implementation. | |
14 */ | |
15 class Sort { | |
16 // When a list has less then [:_INSERTION_SORT_THRESHOLD:] elements it will | |
17 // be sorted by an insertion sort. | |
18 static const int _INSERTION_SORT_THRESHOLD = 32; | |
19 | |
20 /** | |
21 * Sorts all elements of the given list [:a:] according to the given | |
22 * [:compare:] function. | |
23 * | |
24 * The [:compare:] function takes two arguments [:x:] and [:y:] and returns | |
25 * -1 if [:x < y:], | |
26 * 0 if [:x == y:], and | |
27 * 1 if [:x > y:]. | |
28 * | |
29 * The function's behavior must be consistent. It must not return different | |
30 * results for the same values. | |
31 */ | |
32 static void sort(List a, int compare(a, b)) { | |
33 _doSort(a, 0, a.length - 1, compare); | |
34 } | |
35 | |
36 /** | |
37 * Sorts all elements in the range [:from:] (inclusive) to [:to:] (exclusive) | |
38 * of the given list [:a:]. | |
39 * | |
40 * If the given range is invalid an "OutOfRange" error is raised. | |
41 * TODO(floitsch): do we want an error? | |
42 * | |
43 * See [:sort:] for requirements of the [:compare:] function. | |
44 */ | |
45 static void sortRange(List a, int from, int to, int compare(a, b)) { | |
46 if ((from < 0) || (to > a.length) || (to < from)) { | |
47 throw "OutOfRange"; | |
48 } | |
49 _doSort(a, from, to - 1, compare); | |
50 } | |
51 | |
52 /** | |
53 * Sorts the list in the interval [:left:] to [:right:] (both inclusive). | |
54 */ | |
55 static void _doSort(List a, int left, int right, int compare(a, b)) { | |
56 if ((right - left) <= _INSERTION_SORT_THRESHOLD) { | |
57 insertionSort_(a, left, right, compare); | |
58 } else { | |
59 _dualPivotQuicksort(a, left, right, compare); | |
60 } | |
61 } | |
62 | |
63 static void insertionSort_(List a, int left, int right, int compare(a, b)) { | |
64 for (int i = left + 1; i <= right; i++) { | |
65 var el = a[i]; | |
66 int j = i; | |
67 while ((j > left) && (compare(a[j - 1], el) > 0)) { | |
68 a[j] = a[j - 1]; | |
69 j--; | |
70 } | |
71 a[j] = el; | |
72 } | |
73 } | |
74 | |
75 static void _dualPivotQuicksort(List a, | |
76 int left, int right, | |
77 int compare(a, b)) { | |
78 assert(right - left > _INSERTION_SORT_THRESHOLD); | |
79 | |
80 // Compute the two pivots by looking at 5 elements. | |
81 int sixth = (right - left + 1) ~/ 6; | |
82 int index1 = left + sixth; | |
83 int index5 = right - sixth; | |
84 int index3 = (left + right) ~/ 2; // The midpoint. | |
85 int index2 = index3 - sixth; | |
86 int index4 = index3 + sixth; | |
87 | |
88 var el1 = a[index1]; | |
89 var el2 = a[index2]; | |
90 var el3 = a[index3]; | |
91 var el4 = a[index4]; | |
92 var el5 = a[index5]; | |
93 | |
94 // Sort the selected 5 elements using a sorting network. | |
95 if (compare(el1, el2) > 0) { var t = el1; el1 = el2; el2 = t; } | |
96 if (compare(el4, el5) > 0) { var t = el4; el4 = el5; el5 = t; } | |
97 if (compare(el1, el3) > 0) { var t = el1; el1 = el3; el3 = t; } | |
98 if (compare(el2, el3) > 0) { var t = el2; el2 = el3; el3 = t; } | |
99 if (compare(el1, el4) > 0) { var t = el1; el1 = el4; el4 = t; } | |
100 if (compare(el3, el4) > 0) { var t = el3; el3 = el4; el4 = t; } | |
101 if (compare(el2, el5) > 0) { var t = el2; el2 = el5; el5 = t; } | |
102 if (compare(el2, el3) > 0) { var t = el2; el2 = el3; el3 = t; } | |
103 if (compare(el4, el5) > 0) { var t = el4; el4 = el5; el5 = t; } | |
104 | |
105 var pivot1 = el2; | |
106 var pivot2 = el4; | |
107 | |
108 // el2 and el4 have been saved in the pivot variables. They will be written | |
109 // back, once the partioning is finished. | |
110 a[index1] = el1; | |
111 a[index3] = el3; | |
112 a[index5] = el5; | |
113 | |
114 a[index2] = a[left]; | |
115 a[index4] = a[right]; | |
116 | |
117 int less = left + 1; // First element in the middle partition. | |
118 int great = right - 1; // Last element in the middle partition. | |
119 | |
120 bool pivots_are_equal = (compare(pivot1, pivot2) == 0); | |
121 if (pivots_are_equal) { | |
122 var pivot = pivot1; | |
123 // Degenerated case where the partioning becomes a dutch national flag | |
124 // problem. | |
125 // | |
126 // [ | < pivot | == pivot | unpartitioned | > pivot | ] | |
127 // ^ ^ ^ ^ ^ | |
128 // left less k great right | |
129 // | |
130 // a[left] and a[right] are undefined and are filled after the | |
131 // partitioning. | |
132 // | |
133 // Invariants: | |
134 // 1) for x in ]left, less[ : x < pivot. | |
135 // 2) for x in [less, k[ : x == pivot. | |
136 // 3) for x in ]great, right[ : x > pivot. | |
137 for (int k = less; k <= great; k++) { | |
138 var ak = a[k]; | |
139 int comp = compare(ak, pivot); | |
140 if (comp == 0) continue; | |
141 if (comp < 0) { | |
142 if (k != less) { | |
143 a[k] = a[less]; | |
144 a[less] = ak; | |
145 } | |
146 less++; | |
147 } else { | |
148 // comp > 0. | |
149 // | |
150 // Find the first element <= pivot in the range [k - 1, great] and | |
151 // put [:ak:] there. We know that such an element must exist: | |
152 // When k == less, then el3 (which is equal to pivot) lies in the | |
153 // interval. Otherwise a[k - 1] == pivot and the search stops at k-1. | |
154 // Note that in the latter case invariant 2 will be violated for a | |
155 // short amount of time. The invariant will be restored when the | |
156 // pivots are put into their final positions. | |
157 while (true) { | |
158 comp = compare(a[great], pivot); | |
159 if (comp > 0) { | |
160 great--; | |
161 // This is the only location in the while-loop where a new | |
162 // iteration is started. | |
163 continue; | |
164 } else if (comp < 0) { | |
165 // Triple exchange. | |
166 a[k] = a[less]; | |
167 a[less++] = a[great]; | |
168 a[great--] = ak; | |
169 break; | |
170 } else { | |
171 // comp == 0; | |
172 a[k] = a[great]; | |
173 a[great--] = ak; | |
174 // Note: if great < k then we will exit the outer loop and fix | |
175 // invariant 2 (which we just violated). | |
176 break; | |
177 } | |
178 } | |
179 } | |
180 } | |
181 } else { | |
182 // We partition the list into three parts: | |
183 // 1. < pivot1 | |
184 // 2. >= pivot1 && <= pivot2 | |
185 // 3. > pivot2 | |
186 // | |
187 // During the loop we have: | |
188 // [ | < pivot1 | >= pivot1 && <= pivot2 | unpartitioned | > pivot2 | ] | |
189 // ^ ^ ^ ^ ^ | |
190 // left less k great right | |
191 // | |
192 // a[left] and a[right] are undefined and are filled after the | |
193 // partitioning. | |
194 // | |
195 // Invariants: | |
196 // 1. for x in ]left, less[ : x < pivot1 | |
197 // 2. for x in [less, k[ : pivot1 <= x && x <= pivot2 | |
198 // 3. for x in ]great, right[ : x > pivot2 | |
199 for (int k = less; k <= great; k++) { | |
200 var ak = a[k]; | |
201 int comp_pivot1 = compare(ak, pivot1); | |
202 if (comp_pivot1 < 0) { | |
203 if (k != less) { | |
204 a[k] = a[less]; | |
205 a[less] = ak; | |
206 } | |
207 less++; | |
208 } else { | |
209 int comp_pivot2 = compare(ak, pivot2); | |
210 if (comp_pivot2 > 0) { | |
211 while (true) { | |
212 int comp = compare(a[great], pivot2); | |
213 if (comp > 0) { | |
214 great--; | |
215 if (great < k) break; | |
216 // This is the only location inside the loop where a new | |
217 // iteration is started. | |
218 continue; | |
219 } else { | |
220 // a[great] <= pivot2. | |
221 comp = compare(a[great], pivot1); | |
222 if (comp < 0) { | |
223 // Triple exchange. | |
224 a[k] = a[less]; | |
225 a[less++] = a[great]; | |
226 a[great--] = ak; | |
227 } else { | |
228 // a[great] >= pivot1. | |
229 a[k] = a[great]; | |
230 a[great--] = ak; | |
231 } | |
232 break; | |
233 } | |
234 } | |
235 } | |
236 } | |
237 } | |
238 } | |
239 | |
240 // Move pivots into their final positions. | |
241 // We shrunk the list from both sides (a[left] and a[right] have | |
242 // meaningless values in them) and now we move elements from the first | |
243 // and third partition into these locations so that we can store the | |
244 // pivots. | |
245 a[left] = a[less - 1]; | |
246 a[less - 1] = pivot1; | |
247 a[right] = a[great + 1]; | |
248 a[great + 1] = pivot2; | |
249 | |
250 // The list is now partitioned into three partitions: | |
251 // [ < pivot1 | >= pivot1 && <= pivot2 | > pivot2 ] | |
252 // ^ ^ ^ ^ | |
253 // left less great right | |
254 | |
255 // Recursive descent. (Don't include the pivot values.) | |
256 _doSort(a, left, less - 2, compare); | |
257 _doSort(a, great + 2, right, compare); | |
258 | |
259 if (pivots_are_equal) { | |
260 // All elements in the second partition are equal to the pivot. No | |
261 // need to sort them. | |
262 return; | |
263 } | |
264 | |
265 // In theory it should be enough to call _doSort recursively on the second | |
266 // partition. | |
267 // The Android source however removes the pivot elements from the recursive | |
268 // call if the second partition is too large (more than 2/3 of the list). | |
269 if (less < index1 && great > index5) { | |
270 while (compare(a[less], pivot1) == 0) { less++; } | |
271 while (compare(a[great], pivot2) == 0) { great--; } | |
272 | |
273 // Copy paste of the previous 3-way partitioning with adaptions. | |
274 // | |
275 // We partition the list into three parts: | |
276 // 1. == pivot1 | |
277 // 2. > pivot1 && < pivot2 | |
278 // 3. == pivot2 | |
279 // | |
280 // During the loop we have: | |
281 // [ == pivot1 | > pivot1 && < pivot2 | unpartitioned | == pivot2 ] | |
282 // ^ ^ ^ | |
283 // less k great | |
284 // | |
285 // Invariants: | |
286 // 1. for x in [ *, less[ : x == pivot1 | |
287 // 2. for x in [less, k[ : pivot1 < x && x < pivot2 | |
288 // 3. for x in ]great, * ] : x == pivot2 | |
289 for (int k = less; k <= great; k++) { | |
290 var ak = a[k]; | |
291 int comp_pivot1 = compare(ak, pivot1); | |
292 if (comp_pivot1 == 0) { | |
293 if (k != less) { | |
294 a[k] = a[less]; | |
295 a[less] = ak; | |
296 } | |
297 less++; | |
298 } else { | |
299 int comp_pivot2 = compare(ak, pivot2); | |
300 if (comp_pivot2 == 0) { | |
301 while (true) { | |
302 int comp = compare(a[great], pivot2); | |
303 if (comp == 0) { | |
304 great--; | |
305 if (great < k) break; | |
306 // This is the only location inside the loop where a new | |
307 // iteration is started. | |
308 continue; | |
309 } else { | |
310 // a[great] < pivot2. | |
311 comp = compare(a[great], pivot1); | |
312 if (comp < 0) { | |
313 // Triple exchange. | |
314 a[k] = a[less]; | |
315 a[less++] = a[great]; | |
316 a[great--] = ak; | |
317 } else { | |
318 // a[great] == pivot1. | |
319 a[k] = a[great]; | |
320 a[great--] = ak; | |
321 } | |
322 break; | |
323 } | |
324 } | |
325 } | |
326 } | |
327 } | |
328 // The second partition has now been cleared of pivot elements and looks | |
329 // as follows: | |
330 // [ * | > pivot1 && < pivot2 | * ] | |
331 // ^ ^ | |
332 // less great | |
333 // Sort the second partition using recursive descent. | |
334 _doSort(a, less, great, compare); | |
335 } else { | |
336 // The second partition looks as follows: | |
337 // [ * | >= pivot1 && <= pivot2 | * ] | |
338 // ^ ^ | |
339 // less great | |
340 // Simply sort it by recursive descent. | |
341 _doSort(a, less, great, compare); | |
342 } | |
343 } | |
344 } | |
OLD | NEW |