簡體   English   中英

Swift 快速排序算法

[英]Swift Quicksort Algorithm

一段時間以來,我一直試圖在 Swift 中編寫以下快速排序算法,但無法解決該問題。 [快速排序,因為數組中有大約 15,000 個實際值]。 問題是只有數組的左半部分是有序的(見圖),並且該方法永遠不會退出(無限循環)。 遵循從http://www.java2novice.com/java-sorting-algorithms/quick-sort/的 Java 轉換(在 Java 中測試並有效)。 無法解決錯誤。

var arr = [Int]()
var length = 0

override func viewDidLoad()
{
    super.viewDidLoad()

    arr = [5,2,4,7,2,1,3,9,10,11,7,8]
    print(arr)

    sort(inputArr: &arr);

    print(arr)
}

func sort(inputArr: inout [Int])
{

    if (inputArr.count == 0) || (inputArr == nil)
    {
        print("finished")
        return
    }

    arr = inputArr
    length = arr.count
    quicksort(low:0,high:length-1)

}

func quicksort(low: Int, high: Int)
{
    var i = low
    var j = high

    var piv = (low + (high-low))
    piv = piv / 2
    let pivot_location = arr[piv]

    print("----")

    while i <= j
    {
        while arr[i] < pivot_location
        {
            i+=1
        }
        while arr[j] > pivot_location
        {
            j-=1
        }

        if i <= j
        {
            let temp = arr[i]
            arr[i] = arr[j]
            arr[j] = temp

            i+=1
            j-=1
        }
    }

    print(arr)

    if low < j
    {
        quicksort(low: low, high: j)
    }
    if i < high
    {
        quicksort(low: i, high: high)
    }
}

數組的控制台輸出

通過方法迭代后數組的控制台打印

Swift 中的快速排序:

func quicksort<T: Comparable>(_ a: [T]) -> [T] {
  guard a.count > 1 else { return a }

  let pivot = a[a.count/2]
  let less = a.filter { $0 < pivot }
  let equal = a.filter { $0 == pivot }
  let greater = a.filter { $0 > pivot }

  return quicksort(less) + equal + quicksort(greater)
}

使用 15.000 個隨機數進行基准測試(生成數字后計算時間):

  • arr.filter{ $0 < $1 }需要:大約 0.30 分鍾
  • 問題中的代碼需要:大約 1,00 分鍾
  • 我的答案中的快速排序需要:大約 2,00 分鍾

看:

https://github.com/raywenderlich/swift-algorithm-club/tree/master/Quicksort

此處的其他快速排序類型:

https://github.com/raywenderlich/swift-algorithm-club/blob/master/Quicksort/Quicksort.swift

如果需要,您可以使用此代碼進行快速排序,代碼更清晰且易於理解。 試試看

func quickSort2(_ input: [Int], startIndex:Int, endIndex: Int)-> [Int] {
    var inputArray = input
    if startIndex<endIndex {
        let pivot = inputArray[endIndex]
        var index = startIndex

        for demo in startIndex..<endIndex {
            if inputArray[demo] <= pivot {
                (inputArray[index], inputArray[demo]) = (inputArray[demo], inputArray[index])
                index += 1
            }
        }
        (inputArray[index], inputArray[endIndex]) = (inputArray[endIndex], inputArray[index])
        inputArray = quickSort2(inputArray, startIndex: startIndex, endIndex: index-1)
        inputArray = quickSort2(inputArray, startIndex: index+1, endIndex: endIndex)
    }
    return inputArray
}

您對pivpivot_location計算是錯誤的。 它應該是:

let piv = (low + (high - low) / 2)
let pivot_location = arr[piv]    

請注意,我在之前的計算中將除法移動了 2。

輸出:

[5, 2, 4, 7, 2, 1, 3, 9, 10, 11, 7, 8]
[1, 2, 4, 7, 2, 5, 3, 9, 10, 11, 7, 8]
[1, 2, 3, 2, 7, 5, 4, 9, 10, 11, 7, 8]
[1, 2, 2, 3, 7, 5, 4, 9, 10, 11, 7, 8]
[1, 2, 2, 3, 7, 5, 4, 9, 10, 11, 7, 8]
[1, 2, 2, 3, 7, 5, 4, 8, 7, 11, 10, 9]
[1, 2, 2, 3, 4, 5, 7, 8, 7, 11, 10, 9]
[1, 2, 2, 3, 4, 5, 7, 8, 7, 11, 10, 9]
[1, 2, 2, 3, 4, 5, 7, 8, 7, 11, 10, 9]
[1, 2, 2, 3, 4, 5, 7, 7, 8, 11, 10, 9]
[1, 2, 2, 3, 4, 5, 7, 7, 8, 9, 10, 11]
[1, 2, 2, 3, 4, 5, 7, 7, 8, 9, 10, 11]

細節

  • 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 stackoverflow_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

快速排序代碼:

func quickSort<T: Comparable>(_ array: [T]) -> [T] {
    if array.count < 2 {
        return array
    }

    let pivot = array[0]
    print("pivot : \(pivot)")
    
    let smaller = array.filter { $0 < pivot }
    let larger = array.filter { $0 > pivot }

    let result = quickSort(smaller) + [pivot] + quickSort(larger)
    print("result : \(result)")
  
    return result
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM