diff options
| author | Markus Everling <markuseverling@gmail.com> | 2023-01-17 19:38:37 +0100 |
|---|---|---|
| committer | Markus Everling <markuseverling@gmail.com> | 2023-01-17 19:38:37 +0100 |
| commit | 273c6c3913e4d32c3321a3c92fb2ce32c1db9cb8 (patch) | |
| tree | d52baca32729bb1ce44871d9f8cb98fabf1b6350 | |
| parent | 38a76f33220c4b9d13dda1fa8f6c629c8a7bcc5d (diff) | |
| download | rust-273c6c3913e4d32c3321a3c92fb2ce32c1db9cb8.tar.gz rust-273c6c3913e4d32c3321a3c92fb2ce32c1db9cb8.zip | |
Add heapsort fallback in `select_nth_unstable`
| -rw-r--r-- | library/core/src/slice/sort.rs | 22 |
1 files changed, 22 insertions, 0 deletions
diff --git a/library/core/src/slice/sort.rs b/library/core/src/slice/sort.rs index 4d2fcd91784..3ac01d17275 100644 --- a/library/core/src/slice/sort.rs +++ b/library/core/src/slice/sort.rs @@ -831,6 +831,15 @@ fn partition_at_index_loop<'a, T, F>( ) where F: FnMut(&T, &T) -> bool, { + // Limit the amount of iterations and fall back to heapsort, similarly to `slice::sort_unstable`. + // This lowers the worst case running time from O(n^2) to O(n log n). + // FIXME: Investigate whether it would be better to use something like Median of Medians + // or Fast Deterministic Selection to guarantee O(n) worst case. + let mut limit = usize::BITS - v.len().leading_zeros(); + + // True if the last partitioning was reasonably balanced. + let mut was_balanced = true; + loop { // For slices of up to this length it's probably faster to simply sort them. const MAX_INSERTION: usize = 10; @@ -839,6 +848,18 @@ fn partition_at_index_loop<'a, T, F>( return; } + if limit == 0 { + heapsort(v, is_less); + return; + } + + // If the last partitioning was imbalanced, try breaking patterns in the slice by shuffling + // some elements around. Hopefully we'll choose a better pivot this time. + if !was_balanced { + break_patterns(v); + limit -= 1; + } + // Choose a pivot let (pivot, _) = choose_pivot(v, is_less); @@ -863,6 +884,7 @@ fn partition_at_index_loop<'a, T, F>( } let (mid, _) = partition(v, pivot, is_less); + was_balanced = cmp::min(mid, v.len() - mid) >= v.len() / 8; // Split the slice into `left`, `pivot`, and `right`. let (left, right) = v.split_at_mut(mid); |
