【问题标题】:Find connected components in array查找数组中的连通分量
【发布时间】:2018-03-10 19:03:07
【问题描述】:

我想使用 Swift 应用某种特殊的模式查找算法。

一些解释:

我得到一个简单的一维数组,看起来像这样:

var array = [ 
               "0000000000000000000",
               "0000001110000000000",
               "0000011111000000000",
               "0000001110000000000",
               "0000000000000000000",
               "0001100000000000000",
               "0001100000000011000",
               "0011100000000011000",
               "0000000000000000000"
            ]

我想提取“1”字符的连接区域(连接组件)。

看看这个:

       111
      11111
       111

 11
 11         11
111         11

我希望得到一个包含单个组件的所有 x/y 位置的多维数组。

var result = [
                [ [6,1], [7,1], [8,1], [5,2], [6,2], [7,2], [8,2], [9,2], [6,3], [7,3], [8,2] ] // positions of the first area (the biggest one on top)

                [ [3,5], [4,5], [3,6], [4,6], [2,7], [3,7], [4,7] ] // area bottom left

                [ [14,6], [15,6], [14,7], [15,7] ] // area bottom right (smallest area)
             ]

我已经为 javascript 编写了函数。你可以在这里找到代码:

var matrix = [
  "0000000000000000000",
  "0000001110000000000",
  "0000011111000000000",
  "0000001110000000000",
  "0000000000000000000",
  "0001100000000000000",
  "0001100000000011000",
  "0011100000000011000",
  "0000000000000000000"
]


Array.prototype.extract_components_positions = function(offset) {
  var array = this.map(item => item.split('')).map(str => Array.from(str, Number)),
    default_value = 0,
    result_object = {}

  function test_connection(array, i, j) {
    if (array[i] && array[i][j] === -1) {
      if (!result_object[default_value]) result_object[default_value] = [];
      result_object[default_value].push([j, i]);
      array[i][j] = 1;
      for (var k = offset; k > 0; k--) {
        test_connection(array, i + k, j); // left - right
        test_connection(array, i, j + k); // top - bottom
        test_connection(array, i - k, j); // right - left
        test_connection(array, i, j - k); // bottom - top
      }
      return true
    }
  }
  array.forEach(function(a) {
    a.forEach(function(b, i, bb) {
      bb[i] = -b
    })
  });
  array.forEach(function(a, i, aa) {
    a.forEach(function(b, j, bb) {
      test_connection(aa, i, j) && default_value++
    })
  })
  return [result_object];
}



var result = matrix.extract_components_positions(1);
console.log(JSON.stringify(result))

但我在将这段 Javascript 代码翻译成 Swift 时遇到了很大的问题!

 func extract_components_positions(matrix: [[String]],offset: Int) {
    var array = [[]]  // no idea how to use map to split the array from ["0011100"],... to ["0","0","1","1",...], ...
    var default_value = 0,
        result_object = [[Int]()]

    func testconnection(matrix: [[String]], i: Int, j: Int) -> [[Int]] {
        if (Int(array[i][j] as! Int) == -1) {
            array[i][j] = 1
            for var k in offset...0 {
                testconnection(matrix: array, i: i+k, j: j) // error: "Cannot convert value of type '[[Any]]' to expected argument type '[[String]]'"
                testconnection(matrix: array, i: i, j: j+k)
                testconnection(matrix: array, i: i-k, j: j)
                testconnection(matrix: array, i: i, j: j-k)
            }
        }
    }
    array.forEach { (a) in
        a.forEach({ (b, i, bb) in // error: "Contextual closure type '(Any) -> Void' expects 1 argument, but 3 were used in closure body"
            bb[i] = -b
        })
    }
    array.forEach { (a, i, aa) in // error: "Contextual closure type '([Any]) -> Void' expects 1 argument, but 3 were used in closure body"
        a.forEach({ (b, j, bb) in
            testconnection(aa, i, j) && default_value++
        })
    }
    return result_object
}

任何关于如何修复我的代码的帮助将不胜感激。

【问题讨论】:

  • 我不懂 Javascript,所以很难判断你的代码在做什么。如果您想将["aaa", "bbb", "ccc"] 转换为像[["a", "a", "a"], ["b", "b", "b"], ["c", "c", "c"]] 这样的嵌套数组,您可以使用let array = matrix.map { Array($0) }。如果你想要一个一维数组,使用let array = matrix.flatMap { Array($0) }
  • 已经自己找到了一个可行的解决方案@matt

标签: arrays swift algorithm math


【解决方案1】:

看起来你在玩扫雷!这是我的解决方案(在 swift 4.0、Xcode 9.2 中)。有关说明,请参阅内联 cmets。

let array = [
    "0000000000000000000",
    "0000001110000000000",
    "0000011111000000000",
    "0000001110000000000",
    "0000000000000000000",
    "0001100000000000000",
    "0001100000000011000",
    "0011100000000011000",
    "0000000000000000000"
]

// A structure to hold the cell's coordinate as Int array
// can become confusing very quickly
struct Cell: Equatable {
    var row: Int
    var column: Int
    var clusterIndex: Int?

    static func == (lhs: Cell, rhs: Cell) -> Bool {
        return lhs.row == rhs.row && lhs.column == rhs.column
    }
}

// Get all the "1" cells
var cells = array.enumerated().flatMap { arg -> [Cell] in
    let (rowIndex, str) = arg

    // The flatMap below will become compactMap in Swift 4.1
    return str.enumerated().flatMap { colIndex, char in
        if char == "1" {
            return Cell(row: rowIndex, column: colIndex, clusterIndex: nil)
        } else {
            return nil
        }
    }
}

// Assign each cell a clusterIndex
for (i, currentCell) in cells.enumerated() {

    // A cell may not have all four neighbors, or not all its
    // neighbors are "1" cells, hence the "potential"
    let potentialNeighbors = [
        Cell(row: currentCell.row - 1, column: currentCell.column, clusterIndex: nil), // above
        Cell(row: currentCell.row + 1, column: currentCell.column, clusterIndex: nil), // below
        Cell(row: currentCell.row, column: currentCell.column - 1, clusterIndex: nil), // left
        Cell(row: currentCell.row, column: currentCell.column + 1, clusterIndex: nil)  // right
    ]

    // Get the actual neighboring cells and their indexes
    let neighborsAndIndexes = cells.enumerated().filter { arg in
        let (_, c) = arg
        return potentialNeighbors.contains(c)
    }
    let neighborIndexes = neighborsAndIndexes.map { $0.0 }
    let neighbors = neighborsAndIndexes.map { $0.1 }

    // Determine what clusterIndex we should give the current cell and its neighbors
    var clusterIndex = 0

    if currentCell.clusterIndex != nil {
        // If the current cell already has a clusteredIndex, reuse it
        clusterIndex = currentCell.clusterIndex!
    } else if let neighborClusterIndex = neighbors.first(where: { $0.clusterIndex != nil })?.clusterIndex {
        // If the current cell has a neighbor whose clusterIndex is not nil, use that
        clusterIndex = neighborClusterIndex
    } else {
        // Else increment from the max existing clusterIndex
        clusterIndex = (cells.map({ $0.clusterIndex ?? 0 }).max() ?? 0) + 1
    }

    // Assign the same clusterIndex to the current cell and its neighbors
    ([i] + neighborIndexes).forEach {
        cells[$0].clusterIndex = clusterIndex
    }
}

// Group the cells by their clusterIndex
let clusters = Dictionary(grouping: cells, by: { $0.clusterIndex! })
    .sorted(by: { $0.key < $1.key })
    .map { $0.value }

// Print the result
// Visualize which cell belong to which cluster and how it appears on the board
for i in 0..<array.count {
    for j in 0..<array[0].count {
        if let clusterIndex = cells.first(where: { $0.row == i && $0.column == j })?.clusterIndex {
            print(clusterIndex, terminator: "")
        } else {
            print("-", terminator: "")
        }
    }
    print() // print a newline
}

结果:

-------------------
------111----------
-----11111---------
------111----------
-------------------
---22--------------
---22---------33---
--222---------33---
-------------------

请注意,在 Swift 4.1(目前处于测试阶段)中,我们在此处使用的 flatMap 已重命名为 compactMap。这并不是说flatMap 将完全消失。 flatMap 有 3 个版本,其中只有 1 个版本被重命名为 compactMap。如需更多信息,请参阅SE-0187

【讨论】:

  • 您提供的解决方案是我的问题的一个很好的解决方案,但实际上不是在矩阵中查找组件的最有效方法。矩阵格式 50x/50y 可能会起作用,但如果给定更大的矩阵 - 你的函数 那个 很慢而且无效:( 无论如何都非常感谢!
  • 复杂度约为 O(n^2),其中 n 是“1”单元格的数量。当然,这里有巨大的优化机会。我把这个留给你,因为我不知道矩阵能有多大
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2013-05-28
  • 2011-06-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-12-28
相关资源
最近更新 更多