Skip to main content

a/sort/
selection.rs

1use core::cmp::Ordering::{Equal, Greater, Less};
2
3/// Sorts a slice in place using double-ended selection sort.
4///
5/// Each pass scans the unsorted middle of the slice once, finds both its smallest and its
6/// largest key, moves every element holding the smallest key to the front of that range and
7/// every element holding the largest key to its back. Those elements are then in their final
8/// places, so the next pass scans only what lies between them.
9///
10/// Three refinements keep the algorithm as cheap as selection sort can be:
11///
12/// * **Pairwise min-max scan.** Elements are taken two at a time: the two are compared with
13///   each other first, then only the smaller one is compared with the running minimum and
14///   only the larger one with the running maximum. A range of `m` elements costs
15///   `⌈3m/2⌉ - 2` comparisons per pass instead of the `2m - 2` of two independent scans,
16///   which is the fewest any algorithm can use to find both extremes.
17/// * **Duplicate gathering ("bingo") for free.** Every comparison is a three-way
18///   [`Ord::cmp`], so an element equal to the running minimum or maximum is recognised
19///   without an extra comparison and joins that key's group. A pass therefore retires all
20///   copies of two keys: input with `k` distinct keys finishes in at most `⌈k/2⌉` passes
21///   instead of `⌊n/2⌋`. A pass in which every comparison reports [`Equal`] has proved the whole range
22///   equal and ends the sort, again at no extra cost. An equal pair also skips the
23///   comparison with the maximum whenever its minimum comparison already rules that out.
24/// * **Lazy placement.** A running minimum or maximum that has no duplicate yet is only
25///   remembered by index; elements are moved once a pass is over, or when a second copy of
26///   the current extreme turns up. Input with distinct keys costs at most `n - 1` swaps,
27///   exactly like classic selection sort, and gathering duplicates never costs a comparison.
28///
29/// Distinct keys get no benefit from the refinements beyond the pairwise scan, so the worst
30/// case is any input with distinct keys, and on such input the number of comparisons
31/// depends only on `n`: `n(3n - 2)/8` for even `n` and `3(n² - 1)/8` for odd `n`, about 25%
32/// fewer than the `n(n - 1)/2` of classic selection sort.
33///
34/// # Properties
35///
36/// * **Not stable**: equal elements may be reordered.
37/// * **In place**: `O(1)` auxiliary memory, no allocation.
38/// * **Not adaptive to order**: sorted input costs as much as any other with distinct keys.
39///   It is adaptive to duplicates instead: the fewer distinct keys, the fewer passes.
40///
41/// | Case                          | Comparisons                         | Swaps          |
42/// |-------------------------------|-------------------------------------|----------------|
43/// | Best (all elements equal)     | `n - 1`                             | `0`            |
44/// | `k` distinct keys             | `O(nk)`                             | `O(nk)`        |
45/// | Worst (all keys distinct)     | `n(3n - 2)/8` or `3(n² - 1)/8`      | `≤ n - 1`      |
46///
47/// Gathering duplicates costs at most `2m` swaps in a pass over `m` elements, so input with
48/// many repeated keys trades the `n - 1` swap bound for far fewer comparisons and passes.
49/// Input that is already in non-decreasing order is never moved at all.
50///
51/// Selection sort is quadratic and is mainly of educational value; prefer
52/// [`slice::sort_unstable`] for real workloads.
53///
54/// # Panics
55///
56/// Does not panic by itself. If the [`Ord`] implementation of `T` panics, the panic is
57/// propagated and `arr` is left in an unspecified order, but it still holds every original
58/// element exactly once: elements are only ever exchanged with [`slice::swap`].
59///
60/// # Examples
61///
62/// ```
63/// use a::sort::selection;
64///
65/// let mut v = [5, 1, 4, 2, 8, 0];
66/// selection(&mut v);
67/// assert_eq!(v, [0, 1, 2, 4, 5, 8]);
68///
69/// // Three distinct keys: the first pass retires every 1 and every 3, the second the 2s.
70/// let mut v = [3, 1, 2, 3, 1, 2, 2, 1];
71/// selection(&mut v);
72/// assert_eq!(v, [1, 1, 1, 2, 2, 2, 3, 3]);
73///
74/// let mut words = vec!["pear", "apple", "fig"];
75/// selection(&mut words);
76/// assert_eq!(words, ["apple", "fig", "pear"]);
77/// ```
78pub fn selection<T: Ord>(arr: &mut [T]) {
79    // Everything before `lo` and from `hi` onwards is already in its final position.
80    let (mut lo, mut hi) = (0, arr.len());
81    while hi - lo > 1 {
82        let mut pass = Pass::new(&mut arr[lo..hi]);
83        pass.scan();
84        match pass.finish() {
85            Some((front, back)) => {
86                lo += front;
87                hi -= back;
88            }
89            None => return, // every element still unsorted is equal to every other
90        }
91    }
92}
93
94/// One pass of [`selection`] over the unsorted range `v`.
95///
96/// The scanned prefix `v[..end]` holds the elements equal to the running minimum (the min
97/// group), the elements equal to the running maximum (the max group), and everything else in
98/// no particular order. A group with a single element is only remembered by its index; once
99/// it has two or more, it is kept contiguous:
100///
101/// * the min group occupies `v[..min_len]`, at the front, where it has to end up anyway;
102/// * the max group occupies `v[mb..mb + max_len]`, somewhere after the min group.
103///
104/// While every element scanned so far is equal (`same`), they all belong to the min group and
105/// the max fields are unused.
106struct Pass<'a, T> {
107    v: &'a mut [T],
108    /// Number of elements scanned.
109    end: usize,
110    /// Whether every element scanned so far is equal.
111    same: bool,
112    /// Length of the contiguous min group at the front, or `0` while it is a single element.
113    min_len: usize,
114    /// Index of an element of the min group.
115    mi: usize,
116    /// Length of the contiguous max group, or `0` while it is a single element.
117    max_len: usize,
118    /// Start of the contiguous max group, when there is one.
119    mb: usize,
120    /// Index of an element of the max group.
121    mx: usize,
122    /// Indices of elements still being placed, kept up to date by [`Pass::swap`].
123    held: [usize; 2],
124}
125
126impl<'a, T: Ord> Pass<'a, T> {
127    fn new(v: &'a mut [T]) -> Self {
128        Pass {
129            v,
130            end: 0,
131            same: true,
132            min_len: 0,
133            mi: 0,
134            max_len: 0,
135            mb: 0,
136            mx: 0,
137            held: [0; 2],
138        }
139    }
140
141    /// Scans the whole range, gathering the min and max groups.
142    fn scan(&mut self) {
143        let len = self.v.len();
144        // An odd length starts from a lone element, an even one from a compared pair, so
145        // the rest of the range always splits into whole pairs.
146        if len % 2 == 1 {
147            self.end = 1;
148        } else {
149            self.end = 2;
150            match self.v[0].cmp(&self.v[1]) {
151                Less => {
152                    self.same = false;
153                    self.mx = 1;
154                }
155                Greater => {
156                    self.same = false;
157                    self.mi = 1;
158                }
159                Equal => self.push_min(1),
160            }
161        }
162        while self.end < len {
163            let x = self.end;
164            self.end += 2;
165            self.pair(x, x + 1);
166        }
167    }
168
169    /// Classifies the pair at `x` and `y` with at most three comparisons and places it.
170    fn pair(&mut self, x: usize, y: usize) {
171        // Before the first unequal pair, the minimum is also the maximum.
172        let (min, max) = (self.mi, if self.same { self.mi } else { self.mx });
173        match self.v[x].cmp(&self.v[y]) {
174            Equal => {
175                // Both elements share one key, so one comparison against the minimum often
176                // settles both of them.
177                self.held[0] = y;
178                let joined_min = match self.v[x].cmp(&self.v[min]) {
179                    Less => {
180                        self.new_min(x);
181                        true
182                    }
183                    Equal => {
184                        self.push_min(x);
185                        true
186                    }
187                    // Greater than the minimum while everything so far is equal, so it is
188                    // also greater than the maximum.
189                    Greater if self.same => {
190                        self.new_max(x);
191                        false
192                    }
193                    Greater => match self.v[x].cmp(&self.v[max]) {
194                        Less => return, // both lie strictly between the extremes
195                        Equal => {
196                            self.push_max(x);
197                            false
198                        }
199                        Greater => {
200                            self.new_max(x);
201                            false
202                        }
203                    },
204                };
205                let y = self.held[0];
206                if joined_min {
207                    self.push_min(y);
208                } else {
209                    self.push_max(y);
210                }
211            }
212            order => {
213                let (s, l) = if order == Less { (x, y) } else { (y, x) };
214                // Both comparisons are made against the extremes as they were before the
215                // pair, then the smaller element is placed before the larger one.
216                let below = self.v[s].cmp(&self.v[min]);
217                let above = self.v[l].cmp(&self.v[max]);
218                self.held[0] = l;
219                match below {
220                    Less => self.new_min(s),
221                    Equal => self.push_min(s),
222                    Greater => {}
223                }
224                let l = self.held[0];
225                match above {
226                    Less => {}
227                    // `l` equals the old maximum. If that was also the old minimum, `s` has
228                    // just undercut it, which turned the old min group into the max group.
229                    Equal => self.push_max(l),
230                    Greater => self.new_max(l),
231                }
232            }
233        }
234    }
235
236    /// Makes the element at `src` the new minimum; the old min group becomes ordinary.
237    fn new_min(&mut self, src: usize) {
238        if self.same {
239            // Every element scanned so far was equal, and they are all larger than `src`:
240            // they form the max group now, exactly where they already are.
241            self.same = false;
242            self.max_len = self.min_len;
243            self.mb = 0;
244            self.mx = self.mi;
245        }
246        self.min_len = 0;
247        self.mi = src;
248    }
249
250    /// Makes the element at `src` the new maximum; the old max group becomes ordinary.
251    fn new_max(&mut self, src: usize) {
252        self.same = false;
253        self.max_len = 0;
254        self.mx = src;
255    }
256
257    /// Adds the element at `src`, which equals the minimum, to the min group.
258    fn push_min(&mut self, src: usize) {
259        self.held[1] = src;
260        if self.min_len == 0 {
261            // The minimum's only element so far opens the group at the front of the range.
262            self.make_room_for_min();
263            self.swap(0, self.mi);
264            self.min_len = 1;
265        }
266        self.make_room_for_min();
267        self.swap(self.min_len, self.held[1]);
268        self.min_len += 1;
269        self.mi = 0;
270    }
271
272    /// Frees the slot just after the min group if the max group starts there, by moving the
273    /// max group's first element to just past its end.
274    fn make_room_for_min(&mut self) {
275        // The element being added to the min group belongs to neither group, and it lies
276        // in the scanned prefix but not in front of the max group, so `mb + max_len` is
277        // still inside the scanned prefix.
278        if self.max_len > 0 && self.mb == self.min_len {
279            self.swap(self.mb, self.mb + self.max_len);
280            self.mb += 1;
281            self.mx = self.mb;
282        }
283    }
284
285    /// Adds the element at `src`, which equals the maximum, to the max group.
286    fn push_max(&mut self, src: usize) {
287        if self.max_len == 0 {
288            // The maximum's only element so far opens the group where it already is.
289            self.mb = self.mx;
290            self.max_len = 1;
291        }
292        // Grow the group to the right while that stays inside the scanned prefix. When it
293        // does not, the group ends the prefix and `src` lies before it but after the min
294        // group, so the slot just before the group is free to take.
295        let next = self.mb + self.max_len;
296        let slot = if next < self.end {
297            next
298        } else {
299            self.mb -= 1;
300            self.mb
301        };
302        self.swap(slot, src);
303        self.max_len += 1;
304        self.mx = self.mb;
305    }
306
307    /// Moves the groups into their final places and returns how many elements were placed at
308    /// the front and at the back, or `None` if the whole range turned out to be equal.
309    fn finish(&mut self) -> Option<(usize, usize)> {
310        if self.same {
311            return None;
312        }
313        let len = self.v.len();
314        // The max group goes first: the slot the minimum needs, `v[0]`, may lie inside it.
315        let back = if self.max_len == 0 {
316            self.swap(len - 1, self.mx);
317            1
318        } else {
319            // Only the group's elements outside `v[target..]` move, each into a slot of
320            // `v[target..]` that the group does not already occupy.
321            let (start, count) = (self.mb, self.max_len);
322            let target = len - count;
323            let free = target.max(start + count);
324            for k in 0..(target - start).min(count) {
325                self.swap(start + k, free + k);
326            }
327            count
328        };
329        let front = if self.min_len == 0 {
330            self.swap(0, self.mi);
331            1
332        } else {
333            self.min_len
334        };
335        Some((front, back))
336    }
337
338    /// Swaps two elements and keeps every remembered index pointing at the same element.
339    fn swap(&mut self, a: usize, b: usize) {
340        if a == b {
341            return;
342        }
343        self.v.swap(a, b);
344        let [h0, h1] = &mut self.held;
345        for index in [&mut self.mi, &mut self.mx, h0, h1] {
346            if *index == a {
347                *index = b;
348            } else if *index == b {
349                *index = a;
350            }
351        }
352    }
353}