From 5f470f83beb8db9110dfb81adc88dff5feb506d5 Mon Sep 17 00:00:00 2001 From: Simon Sapin Date: Thu, 3 Jul 2014 20:35:22 +0100 Subject: [PATCH] Add a Quicksort implementation. Quicksort makes different tradeoffs than the existing merge sort: It does not allocate, but is not stable. Original implementation by @pcwalton: https://github.com/mozilla/servo/commit/3764c3c49f --- src/libcollections/slice.rs | 6 +- src/libcollections/vec.rs | 41 +++++++++++- src/libcore/slice.rs | 128 ++++++++++++++++++++++++++++++++++++ src/libcoretest/lib.rs | 1 + src/libcoretest/slice.rs | 40 +++++++++++ src/libstd/prelude.rs | 2 +- 6 files changed, 213 insertions(+), 5 deletions(-) create mode 100644 src/libcoretest/slice.rs diff --git a/src/libcollections/slice.rs b/src/libcollections/slice.rs index 40cf8495a4059..96a60630806dc 100644 --- a/src/libcollections/slice.rs +++ b/src/libcollections/slice.rs @@ -113,7 +113,7 @@ use vec::Vec; pub use core::slice::{ref_slice, mut_ref_slice, Splits, Windows}; pub use core::slice::{Chunks, Vector, ImmutableVector, ImmutableEqVector}; pub use core::slice::{ImmutableOrdVector, MutableVector, Items, MutItems}; -pub use core::slice::{MutSplits, MutChunks}; +pub use core::slice::{MutableOrdVector, MutSplits, MutChunks}; pub use core::slice::{bytes, MutableCloneableVector}; // Functional utilities @@ -589,7 +589,7 @@ impl<'a,T> MutableVectorAllocating<'a, T> for &'a mut [T] { /// Methods for mutable vectors with orderable elements, such as /// in-place sorting. -pub trait MutableOrdVector { +pub trait MutableOrdVectorAllocating { /// Sort the vector, in place. /// /// This is equivalent to `self.sort_by(|a, b| a.cmp(b))`. @@ -635,7 +635,7 @@ pub trait MutableOrdVector { fn prev_permutation(self) -> bool; } -impl<'a, T: Ord> MutableOrdVector for &'a mut [T] { +impl<'a, T: Ord> MutableOrdVectorAllocating for &'a mut [T] { #[inline] fn sort(self) { self.sort_by(|a,b| a.cmp(b)) diff --git a/src/libcollections/vec.rs b/src/libcollections/vec.rs index 2ffc168f82c0e..c8a84bbf0508c 100644 --- a/src/libcollections/vec.rs +++ b/src/libcollections/vec.rs @@ -24,7 +24,8 @@ use core::ptr; use core::uint; use {Collection, Mutable}; -use slice::{MutableOrdVector, MutableVectorAllocating, CloneableVector}; +use slice::{MutableOrdVector, MutableOrdVectorAllocating}; +use slice::{MutableVectorAllocating, CloneableVector}; use slice::{Items, MutItems}; /// An owned, growable vector. @@ -805,6 +806,29 @@ impl Vec { self.as_mut_slice().sort_by(compare) } + /// Sort the vector, in place, using `compare` to compare elements, + /// using the Quicksort algorithm. + /// + /// This sort is `O(n log n)` average-case and does not allocate memory, + /// but is `O(n^2)` worst-case and is *not* stable. + /// See the `sort_by` method for a stable alternative. + /// + /// # Example + /// + /// ```rust + /// let mut v = vec!(5i, 4, 1, 3, 2); + /// v.quicksort_by(|a, b| a.cmp(b)); + /// assert_eq!(v, vec!(1i, 2, 3, 4, 5)); + /// + /// // reverse sorting + /// v.quicksort_by(|a, b| b.cmp(a)); + /// assert_eq!(v, vec!(5i, 4, 3, 2, 1)); + /// ``` + #[inline] + pub fn quicksort_by(&mut self, compare: |&T, &T| -> Ordering) { + self.as_mut_slice().quicksort_by(compare) + } + /// Returns a slice of self spanning the interval [`start`, `end`). /// /// # Failure @@ -1302,6 +1326,21 @@ impl Vec { pub fn sort(&mut self) { self.as_mut_slice().sort() } + + /// Sort the vector, in place, using the Quicksort algorithm. + /// + /// This is equivalent to `self.quicksort_by(|a, b| a.cmp(b))`. + /// + /// # Example + /// + /// ```rust + /// let mut vec = vec!(3i, 1, 2); + /// vec.quicksort(); + /// assert_eq!(vec, vec!(1, 2, 3)); + /// ``` + pub fn quicksort(&mut self) { + self.as_mut_slice().quicksort() + } } impl Mutable for Vec { diff --git a/src/libcore/slice.rs b/src/libcore/slice.rs index 0178c0318b81c..4e5e48c0dc365 100644 --- a/src/libcore/slice.rs +++ b/src/libcore/slice.rs @@ -384,6 +384,84 @@ impl<'a,T> ImmutableVector<'a, T> for &'a [T] { } } +// FIXME(SimonSapin): Make `compare` a by-ref closure when that’s supported, +// and remove the return value. +fn quicksort_helper<'a, T>(arr: &mut [T], left: int, right: int, + compare: |&T, &T|: 'a -> Ordering) + -> |&T, &T|: 'a -> Ordering { + if right <= left { + return compare + } + + let mut i: int = left - 1; + let mut j: int = right; + let mut p: int = i; + let mut q: int = j; + unsafe { + let v: *mut T = &mut arr[right as uint]; + loop { + i += 1; + while compare(&arr[i as uint], &*v) == Less { + i += 1 + } + j -= 1; + while compare(&*v, &arr[j as uint]) == Less { + if j == left { + break + } + j -= 1; + } + if i >= j { + break + } + arr.swap(i as uint, j as uint); + if compare(&arr[i as uint], &*v) == Equal { + p += 1; + arr.swap(p as uint, i as uint) + } + if compare(&*v, &arr[j as uint]) == Equal { + q -= 1; + arr.swap(j as uint, q as uint) + } + } + } + + arr.swap(i as uint, right as uint); + j = i - 1; + i += 1; + let mut k: int = left; + while k < p { + arr.swap(k as uint, j as uint); + k += 1; + j -= 1; + assert!(k < arr.len() as int); + } + k = right - 1; + while k > q { + arr.swap(i as uint, k as uint); + k -= 1; + i += 1; + assert!(k != 0); + } + + let compare = quicksort_helper(arr, left, j, compare); + let compare = quicksort_helper(arr, i, right, compare); + compare +} + +/// An in-place quicksort. +/// +/// The algorithm is from Sedgewick and Bentley, "Quicksort is Optimal": +/// http://www.cs.princeton.edu/~rs/talks/QuicksortIsOptimal.pdf +fn quicksort(arr: &mut [T], compare: |&T, &T| -> Ordering) { + if arr.len() <= 1 { + return + } + + let len = arr.len(); + quicksort_helper(arr, 0, (len - 1) as int, compare); +} + /// Extension methods for vectors such that their elements are /// mutable. pub trait MutableVector<'a, T> { @@ -534,6 +612,26 @@ pub trait MutableVector<'a, T> { /// ``` fn reverse(self); + /// Sort the vector, in place, using `compare` to compare elements, + /// using the Quicksort algorithm. + /// + /// This sort is `O(n log n)` average-case and does not allocate memory, + /// but is `O(n^2)` worst-case and is *not* stable. + /// See the `sort_by` method for a stable alternative. + /// + /// # Example + /// + /// ```rust + /// let mut v = [5i, 4, 1, 3, 2]; + /// v.quicksort_by(|a, b| a.cmp(b)); + /// assert!(v == [1, 2, 3, 4, 5]); + /// + /// // reverse sorting + /// v.quicksort_by(|a, b| b.cmp(a)); + /// assert!(v == [5, 4, 3, 2, 1]); + /// ``` + fn quicksort_by(self, compare: |&T, &T| -> Ordering); + /// Returns an unsafe mutable pointer to the element in index unsafe fn unsafe_mut_ref(self, index: uint) -> &'a mut T; @@ -711,6 +809,11 @@ impl<'a,T> MutableVector<'a, T> for &'a mut [T] { } } + #[inline] + fn quicksort_by(self, compare: |&T, &T| -> Ordering) { + quicksort(self, compare) + } + #[inline] unsafe fn unsafe_mut_ref(self, index: uint) -> &'a mut T { transmute((self.repr().data as *mut T).offset(index as int)) @@ -739,6 +842,31 @@ impl<'a,T> MutableVector<'a, T> for &'a mut [T] { } } +/// Methods for mutable vectors with orderable elements, such as +/// in-place sorting. +pub trait MutableOrdVector { + /// Sort the vector, in place, using the Quicksort algorithm. + /// + /// This is equivalent to `self.quicksort_by(|a, b| a.cmp(b))`. + /// + /// # Example + /// + /// ```rust + /// let mut v = [-5i, 4, 1, -3, 2]; + /// + /// v.quicksort(); + /// assert!(v == [-5i, -3, 1, 2, 4]); + /// ``` + fn quicksort(self); +} + +impl<'a, T: Ord> MutableOrdVector for &'a mut [T] { + #[inline] + fn quicksort(self) { + self.quicksort_by(|a, b| a.cmp(b)) + } +} + /// Extension methods for vectors contain `PartialEq` elements. pub trait ImmutableEqVector { /// Find the first index containing a matching value diff --git a/src/libcoretest/lib.rs b/src/libcoretest/lib.rs index 3a3cac542c91b..586082b67c6be 100644 --- a/src/libcoretest/lib.rs +++ b/src/libcoretest/lib.rs @@ -28,4 +28,5 @@ mod option; mod ptr; mod raw; mod result; +mod slice; mod tuple; diff --git a/src/libcoretest/slice.rs b/src/libcoretest/slice.rs new file mode 100644 index 0000000000000..7ca677a134456 --- /dev/null +++ b/src/libcoretest/slice.rs @@ -0,0 +1,40 @@ +// Copyright 2014 The Rust Project Developers. See the COPYRIGHT +// file at the top-level directory of this distribution and at +// http://rust-lang.org/COPYRIGHT. +// +// Licensed under the Apache License, Version 2.0 or the MIT license +// , at your +// option. This file may not be copied, modified, or distributed +// except according to those terms. + +use std::rand::{Rng, task_rng}; + + +#[test] +fn test_quicksort() { + for len in range(4u, 25) { + for _ in range(0i, 100) { + let mut v = task_rng().gen_iter::().take(len) + .collect::>(); + let mut v1 = v.clone(); + + v.as_mut_slice().quicksort(); + assert!(v.as_slice().windows(2).all(|w| w[0] <= w[1])); + + v1.as_mut_slice().quicksort_by(|a, b| a.cmp(b)); + assert!(v1.as_slice().windows(2).all(|w| w[0] <= w[1])); + + v1.as_mut_slice().quicksort_by(|a, b| b.cmp(a)); + assert!(v1.as_slice().windows(2).all(|w| w[0] >= w[1])); + } + } + + // shouldn't fail/crash + let mut v: [uint, .. 0] = []; + v.quicksort(); + + let mut v = [0xDEADBEEFu]; + v.quicksort(); + assert!(v == [0xDEADBEEF]); +} diff --git a/src/libstd/prelude.rs b/src/libstd/prelude.rs index 61e8b63af359e..bb5f5ec3f290c 100644 --- a/src/libstd/prelude.rs +++ b/src/libstd/prelude.rs @@ -86,7 +86,7 @@ #[doc(no_inline)] pub use slice::{ImmutableVector, MutableVector}; #[doc(no_inline)] pub use slice::{ImmutableEqVector, ImmutableOrdVector}; #[doc(no_inline)] pub use slice::{Vector, VectorVector}; -#[doc(no_inline)] pub use slice::MutableVectorAllocating; +#[doc(no_inline)] pub use slice::{MutableVectorAllocating, MutableOrdVectorAllocating}; #[doc(no_inline)] pub use string::String; #[doc(no_inline)] pub use vec::Vec;