如何更快速地转置数组?

Posted

技术标签:

【中文标题】如何更快速地转置数组?【英文标题】:How to transpose an array more Swiftly? 【发布时间】:2017-02-14 19:40:05 【问题描述】:

我想像这样转置一个数组数组:

原始数组:

[
    [1,2,3],
    [4,5,6],
    [7,8,9]
]

结果:

[
    1,4,7,
    2,5,8,
    3,6,9
]
[1,4,7,2,5,8,3,6,9]

假设所有子数组的长度相同。

如果您还没有注意到,结果中的前三个项目是三个子数组的第一个项目。结果中的第四、五、六项是每个子数组的第二项。

如果你还是不明白,也许这会有所帮助:

目前,我有这个:

func flatten(array: [[Int]]) -> [Int] 
    var flat = [Int]()
    for i in 0..<array[0].count 
        for subarray in array 
            flat.append(subarray[i])
        
    
    return flat

我认为这不是很灵活。我怎样才能快速做到这一点?

只是为了避免成为 XY 问题,这就是我想要这样做的原因。

我正在开发一款棋盘游戏。我正在使用来自HLSpriteKit 的HLGridNode(它基本上是一堆网格状布局的正方形)作为棋盘游戏的棋盘。要编辑网格节点的内容,我需要传入精灵节点的一维数组,而不是二维数组。

为了让我的生活更轻松,我将模型对象存储在二维数组中。这样,我可以通过执行以下操作来引用左侧 5 个正方形和顶部 2 个正方形的正方形:

modelObjects[5][2]

如果我使用 .flatMap $0 展平二维数组并将结果传递给网格节点,modelObjects[5][2] 将显示为距左侧 2 个正方形,距顶部 5 个正方形。

这不是this 的副本,因为该问题似乎有一定数量的数组可供使用。虽然我可以将我的二维数组放入一个循环中,然后做那些enumerate().map ... 的东西,但这似乎是一个非常冗长的方法。我认为必须有一个更简单的方法来使用 2D 数组。

【问题讨论】:

Combining multiple arrays into one, indexing sequentially的可能重复 “我正在开发一款棋盘游戏。”这就是您要解决的问题。你已经解决了数组问题。再花时间在它上面会分散你真正目标的注意力。这并不是说这个问题不有趣。解决问题并不重要。 重新编辑:快速浏览一下“重复的候选者”,在我看来,它的答案假设有一定数量的数组可以使用。 (同意重复标记)注意:您似乎想要转置给定的二维数组,然后简单地展平转置后的数组。看看SwiftSequence 库,它就包含这样一个转置函数(尽管是 Swift 2)。 【参考方案1】:

这是对Shadow Of的answer的改进:

extension Collection where Self.Iterator.Element: RandomAccessCollection 
    // PRECONDITION: `self` must be rectangular, i.e. every row has equal size.
    func transposed() -> [[Self.Iterator.Element.Iterator.Element]] 
        guard let firstRow = self.first else  return [] 
        return firstRow.indices.map  index in
            self.map $0[index] 
        
    


let matrix = [
    [1, 2, 3, 4],
    [5, 6, 7, 8],
    [9, 10, 11, 12],
]
matrix.transposed().forEach print($0) 

【讨论】:

只有在扩展中才能成为 Swifty ;) 当我尝试编写扩展时,我很生气。尝试使用map 导致“分段错误”而没有明显的原因,尝试使用indicies 导致烦人的“无法下标”.. 最后我得到了这个工作版本。我知道如何在 swift 2 中编写它,但是在 swift 3 中它看起来很丑。你能看看我更新的答案,并指出一些错误或更简单的方法吗? 稍后让我试一试 鉴于 OP 希望将 2D 数组展平和转置,您可以将第一个 map 更改为 flatMap - 然后返回 [T] ;) @Hamish 是的,那他会更适合 OP 的问题,但最好让答案的组成部分保持通用和可重用【参考方案2】:

你可以通过转置你的二维矩阵得到你想要的结果,例如,使用这个函数:

func matrixTranspose<T>(_ matrix: [[T]]) -> [[T]] 
    if matrix.isEmpty return matrix
    var result = [[T]]()
    for index in 0..<matrix.first!.count 
        result.append(matrix.map$0[index])
    
    return result

然后应用flattenjoined 在 swift 3 中)。

let arr = [[1,2,3],[4,5,6],[7,8,9]]
print(matrixTranspose(arr))
// [[1, 4, 7], [2, 5, 8], [3, 6, 9]]

print(matrixTranspose(arr).flatMap$0)
// [1, 4, 7, 2, 5, 8, 3, 6, 9]

扩展版本:

extension Collection where Self.Iterator.Element: Collection 
    var transpose: Array<Array<Self.Iterator.Element.Iterator.Element>> 
        var result = Array<Array<Self.Iterator.Element.Iterator.Element>>()
        if self.isEmpty return result

        var index = self.first!.startIndex
        while index != self.first!.endIndex 
            var subresult = Array<Self.Iterator.Element.Iterator.Element>()
            for subarray in self 
                subresult.append(subarray[index])
            
            result.append(subresult)
            index = self.first!.index(after: index)
        
        return result
    

有用法

let arr = [[1,2,3],[4,5,6],[7,8,9]]
print(arr.transpose)
// [[1, 4, 7], [2, 5, 8], [3, 6, 9]]

【讨论】:

尽量避免0 ..&lt; array.count。您可以使用array.indices。看看我对你的回答的改进。 如果您为Array 进行扩展,您应该能够添加约束Element.Indices.Iterator.Element == Element.Index(参见this Q&A)并使用@AlexanderMomchliov 的扩展实现(替换matrix self)。

以上是关于如何更快速地转置数组?的主要内容,如果未能解决你的问题,请参考以下文章

在 Python 中懒惰地转置一个列表

有没有一种方法可以简单地转置 SQL 中的表。此表包含 Numeric 和 Varchar 值

SDUT 3347 数据结构实验之数组三:快速转置

数组的shape属性与矩阵转置的区别在哪里?

使用 SSE、AVX 和 OpenMP 进行快速内存转置

用C试一下稀疏矩阵的快速转置