【问题标题】:How to generate all permutations of splitting an array to two subarrays in Scala?如何在Scala中生成将数组拆分为两个子数组的所有排列?
【发布时间】:2023-03-25 08:15:01
【问题描述】:

给定一个数组,我想把它分成两个非空子数组。

例如,给定以下数组:

val nums = Array(1, 2, 3, 4)

我想生成以下独特的排列:

(Array(1), Array(2, 3, 4))
(Array(2), Array(1, 3, 4))
(Array(1, 2), Array(3, 4))
(Array(3), Array(1, 2, 4))
(Array(1, 3), Array(2, 4))
(Array(2, 3), Array(1, 4))
(Array(1, 2, 3), Array(4))

排列是独一无二的,如果一个给定的拆分只是另一个拆分的镜像,我只需要保留其中一个。此外,子数组中元素的顺序很重要。

请参阅下面的有效解决方案:https://stackoverflow.com/a/57262577/5767875。 但我相信存在更优雅、更实用的解决方案。

【问题讨论】:

    标签: scala


    【解决方案1】:
    def splits(xs: Array[Int]): Array[(Array[Int], Array[Int])] = {
      val startSplit: Array[(Array[Int], Array[Int])] = Array((Array(xs.head), Array.empty))
      xs.tail.foldLeft(startSplit) { (splits, x) =>
        splits.flatMap {
          case (left, right) => Array((x +: left, right), (left, x +: right))
        }
      }.tail
    }
    

    基本思想是,如果您知道前 N 个元素的所有拆分,则 N+1 个元素的拆分数量是原来的两倍:您可以将新元素添加到每个拆分的左侧或右侧并获得新的分裂。这就是 foldLeft 通话中发生的事情。

    唯一的小皱纹是:这会产生“镜像”分裂,所以起点总是在左边的第一个元素。并且:最后,您有一个额外的输出,即左侧的每个元素,因此对 .tail 的最终调用消除了这一点。

    请注意,这样做的性能可能非常糟糕,因为 scala 数组没有有效的追加,因此每个操作都会复制。您可以在上面的代码中将Array 替换为List 并获得更好的结果。如果你真的需要处理数组,你可以转换 (.toList / .toArray)

    【讨论】:

    • 非常好的解决方案,@Joe K!你是对的,使用List可以加快性能。
    【解决方案2】:

    基于这个SO post,下面是Scala代码的一个版本:

    import scala.collection.mutable.ArrayBuffer
    
    def generateSplitPermutations(nums: Array[Int]): Array[(Array[Int], Array[Int])] = {
      var results: List[(Array[Int], Array[Int])] = List()
    
      var flags = Array.fill(nums.length)(false)
      var done = false
      while (!done) {
    
        var a = ArrayBuffer[Int]()
        var b = ArrayBuffer[Int]()
        for ((bool, i) <- flags.zipWithIndex) {
          if (bool)
            a += nums(i)
          else
            b += nums(i)
        }
    
        if (a.length > 0 && b.length > 0) {
           results = results :+ (a.toArray, b.toArray)
        }
    
        if (flags.map(x => if (x) 1 else 0).sum == nums.length / 2 + 1) {
          done = true
        }
    
        // if done is true, the following code block won't matter
        var ok = false
        for (i <- 0 until nums.length if !ok) {
          flags(i) = !flags(i)
          if (flags(i))
            ok = true
        }
      }
    
      results.toArray
    }
    
    
    val nums = Array(1, 2, 3, 4)
    generateSplitPermutations(nums)
    

    输出:

    scala> generateSplitPermutations(nums)
    res16: Array[(Array[Int], Array[Int])] = Array((Array(1),Array(2, 3, 4)), (Array(2),Array(1, 3, 4)), (Array(1, 2),Array(3, 4)), (Array(3),Array(1, 2, 4)), (Array(1, 3),Array(2, 4)), (Array(2, 3),Array(1, 4)), (Array(1, 2, 3),Array(4)))
    

    是否有更多 Scala/功能性的方式来实现这一点?

    【讨论】:

      猜你喜欢
      • 2018-05-21
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2011-08-16
      • 2021-12-29
      • 2012-04-13
      • 2010-10-12
      相关资源
      最近更新 更多