详情
- Swift 5.2、Xcode 11.4 (11E146)
解决方案
import Foundation
enum QuickSortOrder { case lowToHight, hightToLow }
// MARK: Quik Sort
// time: n*log n
// memory: n*log n
extension RangeReplaceableCollection where Element: Comparable {
func quickSort(by order: QuickSortOrder = .lowToHight) -> Self {
guard count > 1 else { return self }
let pivot = self[index(endIndex, offsetBy: -1)]
var pivotCount = 0
var lessThanPivot = Self()
var greaterThanPivot = Self()
forEach { element in
switch element {
case ..<pivot: lessThanPivot.append(element)
case pivot: pivotCount += 1
default: greaterThanPivot.append(element)
}
}
let equalPivot = Self(repeating: pivot, count: pivotCount)
if equalPivot.count == count {
return equalPivot
} else {
if lessThanPivot.first != nil {
switch order {
case .lowToHight: return Self(lessThanPivot.quickSort(by: order) + equalPivot + greaterThanPivot.quickSort(by: order))
case .hightToLow: return Self(greaterThanPivot.quickSort(by: order) + equalPivot + lessThanPivot.quickSort(by: order))
}
} else {
switch order {
case .lowToHight: return Self(equalPivot + greaterThanPivot.quickSort(by: order))
case .hightToLow: return Self(greaterThanPivot.quickSort(by: order) + equalPivot)
}
}
}
}
}
// MARK: Quik Sort Partition Algorithm
// time: n*log n
// memory: use the same array (0)
extension MutableCollection where Element: Comparable {
mutating
func quickSorted(by order: QuickSortOrder = .lowToHight) {
quickSortSlicing(left: startIndex, right: index(endIndex, offsetBy: -1), by: order)
}
private mutating
func quickSortSlicing(left: Index, right: Index, by order: QuickSortOrder) {
guard left < right else { return }
let index = quickSortSliceElementsReordering(left: left, right: right, by: order)
quickSortSlicing(left: left, right: self.index(index, offsetBy: -1), by: order)
quickSortSlicing(left: index, right: right, by: order)
}
private mutating
func quickSortSliceElementsReordering(left: Index, right: Index, by order: QuickSortOrder) -> Index {
let distance = self.distance(from: left, to: right)
let pivot = self[self.index(left, offsetBy: distance/2)]
var leftIndex = left
var rightIndex = right
while leftIndex <= rightIndex {
switch order {
case .lowToHight:
while self[leftIndex] < pivot { leftIndex = index(leftIndex, offsetBy: 1) }
while self[rightIndex] > pivot {
rightIndex = index(rightIndex, offsetBy: -1)
}
case .hightToLow:
while self[leftIndex] > pivot { leftIndex = index(leftIndex, offsetBy: 1) }
while self[rightIndex] < pivot {
rightIndex = index(rightIndex, offsetBy: -1)
}
}
if leftIndex <= rightIndex {
self.swapAt(leftIndex, rightIndex)
leftIndex = index(leftIndex, offsetBy: 1)
rightIndex = index(rightIndex, offsetBy: -1)
}
}
return leftIndex
}
}
用法
// Option 1
let sortedArray = array.quickSort()
// Option 2
let sortedArray = array.quickSort(by: .lowToHight)
// Option 3
let sortedArray = array.quickSort(by: .hightToLow)
// Option 4
var array = [......]
array.quickSorted()
// Option 5
var array = [......]
array.quickSorted(by: .lowToHight)
// Option 6
var array = [......]
array.quickSorted(by: .hightToLow)
单元测试
import XCTest
//@testable import *_41909806
protocol QuicSortTestable {
associatedtype C: MutableCollection & RangeReplaceableCollection where C:Equatable, C.Element: Comparable, C.Index == Int
typealias Element = C.Element
func generateCollection() -> C
}
extension QuicSortTestable where Self: XCTestCase {
func _test() {
let collection = generateCollection()
_test(collection: collection, sortBy: <, quickSortOrder: .lowToHight)
//_test(collection: collection, sortBy: >, quickSortOrder: .hightToLow)
}
private func measureTime(of closure: (() -> Void)) -> TimeInterval {
let start = ProcessInfo.processInfo.systemUptime
closure()
return ProcessInfo.processInfo.systemUptime - start
}
private func _test(collection: C, sortBy closure: (_ left: Element, _ right: Element) -> Bool, quickSortOrder: QuickSortOrder) {
print("--------------------------\n-- Collection size: \(collection.count), sorting order: \(quickSortOrder)")
var times = [String: TimeInterval]()
var sortedCollection = C()
times["collection.sorted(by:)"] = measureTime (of: { sortedCollection = collection.sorted(by: closure) as! Self.C })
var quickSortedCollection: C!
times["collection.quickSort(by:)"] = measureTime (of: { quickSortedCollection = collection.quickSort(by: quickSortOrder) })
XCTAssertEqual(quickSortedCollection, sortedCollection)
quickSortedCollection = collection
times["collection.quickSorted(by:)"] = measureTime (of: {
quickSortedCollection.quickSorted(by: quickSortOrder)
})
XCTAssertEqual(quickSortedCollection!, sortedCollection)
quickSortedCollection = collection
times["collection.quickSorted(by:)"] = measureTime (of: {
quickSortedCollection.quickSorted(by: quickSortOrder)
})
XCTAssertEqual(quickSortedCollection!, sortedCollection)
let numberFormatter = NumberFormatter()
numberFormatter.numberStyle = .decimal
numberFormatter.maximumFractionDigits = 6
times.forEach { key, value in
print("-- Excecution time of \(key) = \(numberFormatter.string(from: NSNumber(value: value)) ?? "\(value)")")
}
}
}
//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
class SortingAlgorithmsTests_emptyCollection: XCTestCase, QuicSortTestable {
func generateCollection() -> [Int] { [] }
func test() { _test() }
}
class SortingAlgorithmsTests_oneElementCollection: SortingAlgorithmsTests_emptyCollection {
override func generateCollection() -> [Int] { [1] }
}
class SortingAlgorithmsTests_dublicatedElementCollection: SortingAlgorithmsTests_emptyCollection {
override func generateCollection() -> [Int] { [1,1] }
}
class SortingAlgorithmsTests_randomCollection: SortingAlgorithmsTests_emptyCollection {
var generatedArraySize: Int { return 1_000 }
override func generateCollection() -> [Int] {
return (0 ..< generatedArraySize).map { (0...1000).randomElement() ?? $0 }
}
}
class SortingAlgorithmsTests_randomCollection2: SortingAlgorithmsTests_randomCollection {
override var generatedArraySize: Int { return 100_000 }
}
class SortingAlgorithmsTests_randomCollection3: SortingAlgorithmsTests_randomCollection {
override var generatedArraySize: Int { return 1_000_000 }
}
时间测量(单元测试日志)
--------------------------
-- Collection size: 2, sorting order: lowToHight
-- Excecution time of collection.quickSort(by:) = 0.00008
-- Excecution time of collection.quickSorted(by:) = 0.000003
-- Excecution time of collection.sorted(by:) = 0.000109
--------------------------
-- Collection size: 0, sorting order: lowToHight
-- Excecution time of collection.quickSort(by:) = 0.000001
-- Excecution time of collection.quickSorted(by:) = 0
-- Excecution time of collection.sorted(by:) = 0.000007
--------------------------
-- Collection size: 1, sorting order: lowToHight
-- Excecution time of collection.sorted(by:) = 0.00001
-- Excecution time of collection.quickSort(by:) = 0.000002
-- Excecution time of collection.quickSorted(by:) = 0.000001
--------------------------
-- Collection size: 1000, sorting order: lowToHight
-- Excecution time of collection.quickSort(by:) = 0.009461
-- Excecution time of collection.sorted(by:) = 0.008551
-- Excecution time of collection.quickSorted(by:) = 0.006646
--------------------------
-- Collection size: 100000, sorting order: lowToHight
-- Excecution time of collection.quickSorted(by:) = 0.892118
-- Excecution time of collection.sorted(by:) = 0.59183
-- Excecution time of collection.quickSort(by:) = 0.588457
--------------------------
-- Collection size: 1000000, sorting order: lowToHight
-- Excecution time of collection.quickSort(by:) = 5.857336
-- Excecution time of collection.sorted(by:) = 6.957055
-- Excecution time of collection.quickSorted(by:) = 10.518193