diff --git a/README.md b/README.md index 34aa526..bc86404 100644 --- a/README.md +++ b/README.md @@ -8,6 +8,7 @@ The `SortingAlgorithms` package provides three sorting algorithms that can be used with Julia's [standard sorting API](http://docs.julialang.org/en/latest/stdlib/sort/): - [HeapSort] – an unstable, general purpose, in-place, O(n log n) comparison sort that works by heapifying an array and repeatedly taking the maximal element from the heap. +- QuickSort3 - an unstable, general purpose, in-place, O(n log n) comparison sort variation of the classic QuickSort that uses a three-way partion algorithm. It can be 1.1x slower than QuickSort in base but works in near O(n) time when there are few unique values in the array. - [TimSort] – a stable, general purpose, hybrid, O(n log n) comparison sort that adapts to different common patterns of partially ordered input data. - [RadixSort] – a stable, special case, in-place, O(n) non-comparison sort that works by sorting data with fixed size, one digit at a time. diff --git a/src/SortingAlgorithms.jl b/src/SortingAlgorithms.jl index 976174f..d435da7 100644 --- a/src/SortingAlgorithms.jl +++ b/src/SortingAlgorithms.jl @@ -7,15 +7,17 @@ using Base.Order import Base.Sort: sort! import Base.Collections: heapify!, percolate_down! -export HeapSort, TimSort, RadixSort +export HeapSort, TimSort, RadixSort, QuickSort3 -immutable HeapSortAlg <: Algorithm end -immutable TimSortAlg <: Algorithm end -immutable RadixSortAlg <: Algorithm end +immutable HeapSortAlg <: Algorithm end +immutable TimSortAlg <: Algorithm end +immutable RadixSortAlg <: Algorithm end +immutable QuickSort3Alg <: Algorithm end -const HeapSort = HeapSortAlg() -const TimSort = TimSortAlg() -const RadixSort = RadixSortAlg() +const HeapSort = HeapSortAlg() +const TimSort = TimSortAlg() +const RadixSort = RadixSortAlg() +const QuickSort3 = QuickSort3Alg() ## Heap sort @@ -38,6 +40,41 @@ function sort!(v::AbstractVector, lo::Int, hi::Int, a::HeapSortAlg, o::Ordering) end +## Quick sort with 3 way partitioning + +function partition3!(v::AbstractVector, lo::Int, hi::Int, o::Ordering) + p = Base.Sort.selectpivot!(v, lo, hi, o) + i = k = lo + 1; j = hi - 1 + @inbounds while true + while lt(o, v[i], p); i += 1; end + while lt(o, p, v[j]); j -= 1; end + k = max(i, k) + while v[k] == p; k += 1; end + k >= j && break + v[k], v[j] = v[j], v[k] + i = k + end + j -= (i == j) + @inbounds v[j], v[lo] = p, v[j] + return i, j +end + +function sort!(v::AbstractVector, lo::Int, hi::Int, a::QuickSort3Alg, o::Ordering) + @inbounds while lo < hi + hi-lo <= SMALL_THRESHOLD && return sort!(v, lo, hi, SMALL_ALGORITHM, o) + i, j = partition3!(v, lo, hi, o) + if i-lo < hi-j + lo < (i-1) && sort!(v, lo, i-1, a, o) + lo = j+1 + else + j+1 < hi && sort!(v, j+1, hi, a, o) + hi = i-1 + end + end + return v +end + + ## Radix sort # Map a bits-type to an unsigned int, maintaining sort order diff --git a/test/runtests.jl b/test/runtests.jl index 3a9bdbc..4e2406f 100644 --- a/test/runtests.jl +++ b/test/runtests.jl @@ -4,7 +4,7 @@ using Compat a = rand(1:10000, 1000) -for alg in [TimSort, HeapSort, RadixSort] +for alg in [TimSort, HeapSort, RadixSort, QuickSort3] b = sort(a, alg=alg) @test issorted(b) ix = sortperm(a, alg=alg) @@ -84,7 +84,7 @@ for n in [0:10..., 100, 101, 1000, 1001] end # unstable algorithms - for alg in [HeapSort] + for alg in [HeapSort, QuickSort3] p = sortperm(v, alg=alg, order=ord) @test isperm(p) @test v[p] == si @@ -98,7 +98,7 @@ for n in [0:10..., 100, 101, 1000, 1001] v = randn_with_nans(n,0.1) for ord in [Base.Order.Forward, Base.Order.Reverse], - alg in [TimSort, HeapSort, RadixSort] + alg in [TimSort, HeapSort, RadixSort, QuickSort3] # test float sorting with NaNs s = sort(v, alg=alg, order=ord) @test issorted(s, order=ord)