【问题标题】:How do I achieve an in-place sort on a sub range of elements in an Array<T> in swift?如何快速对 Array<T> 中的子元素范围进行就地排序?
【发布时间】:2018-12-22 21:56:36
【问题描述】:

Array 类型的标准排序方法没有变体可以缩小要排序的数组区域。由于写时复制语义,使用 ArraySlice 不会导致原始数据得到排序。

我是否必须编写一个新的排序实现来实现对数组子范围的就地排序?还是有一种丑陋的方法来创建第二个数组,该数组将所需元素范围与原始数组取别名?

【问题讨论】:

  • 你可以写array[from...to].sort(),但我假设这会创建一个临时副本。

标签: swift sorting copy-on-write


【解决方案1】:

subscript operation that takes a range 返回一个可变值。如果您直接在其上调用变异方法,它将原地变异原始数组:

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 中是否会出现这种情况。

【讨论】:

  • 我想我的问题是:这是否比我所说的更“空间复杂”(无论这意味着什么),还是只是语义更清晰?
  • 我不是 100% 确定。我编辑了答案以提供一些背景信息。
  • 很酷,谢谢,只是想澄清一下(在我们可以这样做的范围内)。
  • 如果您想确定,请考虑在Swift Forums 上提问。这就是人们闲逛的地方,他们可以给你一个明确的答案。
  • 嗯,问题只是这个答案是否与我的答案一样受到来自 OP 的“反对”。
【解决方案2】:

很难看出你认为问题出在哪里:

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]

这不是预期的目标吗?

【讨论】:

  • 这是否相当于将子范围复制到新存储,对其进行排序,然后将其复制回来?这将达到预期的结果,但不一定是我正在寻找的空间复杂度。
  • 我不知道什么是空间复杂度,它似乎并没有出现在你原来的问题中。 sorted,甚至sort,无论你做什么都会复制;数组是值类型。
  • 临时的arr[range].sorted() 是辅助空间,需要由这段代码创建。
  • @Carpsen90 但这是不可避免的。它是一种价值类型。避免添加辅助空间的唯一可靠方法是,例如自己实现一个链接列表并根据需要重新指向链接。
  • @matt 空间复杂度是类似于运行时复杂度概念的存储。对需要完整临时副本的数组的操作将具有线性空间复杂度,而不需要(仅需要与原始数组长度无关的临时存储)的操作具有恒定的空间复杂度。
【解决方案3】:

我建议使用自定义快速排序实现:

// 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]

独创的quicksort swift算法source

【讨论】:

    猜你喜欢
    • 2020-06-24
    • 2017-09-04
    • 1970-01-01
    • 2011-11-05
    • 1970-01-01
    • 2011-10-18
    • 2017-01-06
    • 2019-02-07
    • 1970-01-01
    相关资源
    最近更新 更多