如何在 swift 中的 Array<T> 中的子范围内实现就地排序?
How do I achieve an in-place sort on a sub range of elements in an Array<T> in swift?
Array 类型的标准排序方法没有可以缩小要排序的数组区域的变体。由于写时复制语义,使用 ArraySlice 不会导致对原始数据进行排序。
我是否必须编写新的排序实现来实现对数组子范围的就地排序?或者是否有一种丑陋的方法来创建第二个数组,该数组对原始数组的所需元素范围进行别名?
很难看出您认为的问题所在:
var arr = [9,8,7,6,5,4,3,2,1]
let range = 4...7
arr.replaceSubrange(range, with: arr[range].sorted())
print(arr) // [9, 8, 7, 6, 2, 3, 4, 5, 1]
这不是预期的目标吗?
subscript operation that takes a range returns 一个可变值。如果你直接调用它的变异方法,它会改变原来的数组:
var numbers = [3,5,2,4,1]
numbers[1..<4].sort()
print(numbers) // [3, 2, 4, 5, 1]
从 Swift 4.2 开始,我 认为 这并不比制作切片的临时副本更有效(来源:forum post 1, forum post 2。这些帖子讨论各种切片操作,包括改变源集合长度的切片操作,但就地排序应该是一样的。)
即将发布的 Swift 5 版本进行了很多内部更改,旨在提高效率(背景:ownership manifesto)。在 Swift 5 中,一个 setter (就像一个变异的下标)可以被建模为一个协程,这将允许就地执行这种操作。但我不能 100% 确定 Swift 5.0.
中是否会出现这种情况
我建议使用自定义快速排序实现:
// MARK: - Array<Element: Comparable>
extension Array where Element: Comparable {
// MARK: - Internal
mutating func quickSort(subrange: Range<Int>) {
privateQuickSort(left: subrange.lowerBound, right: subrange.upperBound - 1)
}
// MARK: - Private
mutating private func partition(left: Int, right: Int) -> Int {
var i = left
for j in (left + 1)..<(right + 1) {
if self[j] < self[left] {
i += 1
(self[i], self[j]) = (self[j], self[i])
}
}
(self[i], self[left]) = (self[left], self[i])
return i
}
mutating private func privateQuickSort(left: Int, right: Int) {
if right > left {
let pivotIndex = partition(left: left, right: right)
privateQuickSort(left: left, right: pivotIndex - 1)
privateQuickSort(left: pivotIndex + 1, right: right)
}
}
}
var array = [10,0,2,1,4,5,6,7,8,3]
array.quickSort(subrange: 0..<array.count)
print(array)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 10]
但是
var array = [10,0,2,1,4,5,6,7,8,3]
array.quickSort(subrange: 0..<2)
[0, 10, 2, 1, 4, 5, 6, 7, 8, 3]
原始快速排序swift算法source
Array 类型的标准排序方法没有可以缩小要排序的数组区域的变体。由于写时复制语义,使用 ArraySlice 不会导致对原始数据进行排序。
我是否必须编写新的排序实现来实现对数组子范围的就地排序?或者是否有一种丑陋的方法来创建第二个数组,该数组对原始数组的所需元素范围进行别名?
很难看出您认为的问题所在:
var arr = [9,8,7,6,5,4,3,2,1]
let range = 4...7
arr.replaceSubrange(range, with: arr[range].sorted())
print(arr) // [9, 8, 7, 6, 2, 3, 4, 5, 1]
这不是预期的目标吗?
subscript operation that takes a range returns 一个可变值。如果你直接调用它的变异方法,它会改变原来的数组:
var numbers = [3,5,2,4,1]
numbers[1..<4].sort()
print(numbers) // [3, 2, 4, 5, 1]
从 Swift 4.2 开始,我 认为 这并不比制作切片的临时副本更有效(来源:forum post 1, forum post 2。这些帖子讨论各种切片操作,包括改变源集合长度的切片操作,但就地排序应该是一样的。)
即将发布的 Swift 5 版本进行了很多内部更改,旨在提高效率(背景:ownership manifesto)。在 Swift 5 中,一个 setter (就像一个变异的下标)可以被建模为一个协程,这将允许就地执行这种操作。但我不能 100% 确定 Swift 5.0.
中是否会出现这种情况我建议使用自定义快速排序实现:
// MARK: - Array<Element: Comparable>
extension Array where Element: Comparable {
// MARK: - Internal
mutating func quickSort(subrange: Range<Int>) {
privateQuickSort(left: subrange.lowerBound, right: subrange.upperBound - 1)
}
// MARK: - Private
mutating private func partition(left: Int, right: Int) -> Int {
var i = left
for j in (left + 1)..<(right + 1) {
if self[j] < self[left] {
i += 1
(self[i], self[j]) = (self[j], self[i])
}
}
(self[i], self[left]) = (self[left], self[i])
return i
}
mutating private func privateQuickSort(left: Int, right: Int) {
if right > left {
let pivotIndex = partition(left: left, right: right)
privateQuickSort(left: left, right: pivotIndex - 1)
privateQuickSort(left: pivotIndex + 1, right: right)
}
}
}
var array = [10,0,2,1,4,5,6,7,8,3]
array.quickSort(subrange: 0..<array.count)
print(array)
[0, 1, 2, 3, 4, 5, 6, 7, 8, 10]
但是
var array = [10,0,2,1,4,5,6,7,8,3]
array.quickSort(subrange: 0..<2)
[0, 10, 2, 1, 4, 5, 6, 7, 8, 3]
原始快速排序swift算法source