(Scala) Leetcode 200. 超出内存限制

Posted

技术标签:

【中文标题】(Scala) Leetcode 200. 超出内存限制【英文标题】:(Scala) Leetcode 200. Memory limit exceeded 【发布时间】:2020-08-22 21:53:04 【问题描述】:

我正在使用 Scala 来解决此处记录的问题:https://leetcode.com/problems/number-of-islands/

给定一个由“1”(陆地)和“0”(水)组成的二维网格图,计算数量 的岛屿。岛屿四面环水,由 水平或垂直连接相邻土地。你可以假设 网格的四个边缘都被水包围。

示例 1:

输入: 网格 = [ ["1","1","1","1","0"], ["1","1","0","1","0"], ["1","1","0","0","0"], ["0","0","0","0","0"] ] 输出:1

示例 2:

输入: 网格 = [ ["1","1","0","0","0"], ["1","1","0","0","0"], ["0","0","1","0","0"], ["0","0","0","1","1"] ] 输出:3

我认为这是广度(或深度)优先搜索的简单应用。但是,我的程序在 Leetcode 产生了超出内存限制的错误。我还在笔记本电脑上运行了该程序。我的 Mac 报告它使用了超过 3 GB 的内存,即使我已经注释掉了 3 行测试用例。什么在我的代码中消耗了这么多内存?

// Memory OVERFLOW !!? Why ??
class Leet0200 
  def numIslands(grid: Array[Array[Char]]): Int = 
    var (y, x) = getPosition(grid)
    var c = 0
    val dirs = Array(
      Array(1, 0),
      Array(-1, 0),
      Array(0, 1),
      Array(0, -1)
    )
    while ((y, x) != (-1, -1)) 
      c += 1
      val queue = scala.collection.mutable.Queue[(Int, Int)]()
      queue.enqueue((y,x))
      while (queue.nonEmpty) 
        val me = queue.dequeue()
        val (me_y, me_x) = me
        grid(me_y)(me_x) = 'c'
        for (d <- dirs) 
          val (ny, nx) = (me_y + d(0), me_x+d(1))
          if (ny >= 0 && ny < grid.length
              && nx >= 0 && nx < grid(0).length
              && grid(ny)(nx) == '1') 
            queue.enqueue((ny, nx))
          
        
      
      val newPos = getPosition(grid)
      y = newPos._1
      x = newPos._2
    
    c
  

  def getPosition(grid: Array[Array[Char]]): (Int, Int) = 
    for (y <- grid.indices) 
      for (x <- grid(0).indices) 
        if (grid(y)(x) == '1') return (y, x)
      
    
    (-1, -1)
     

object Leet0200 
  def main(args: Array[String]): Unit = 
    val leet = new Leet0200()
    println(
      leet.numIslands(Array(
    Array('1','1','1','1','1','0','1','1','1','1','1','1','1','1','1','0','1','0','1','1'),
        Array('0','1','1','1','1','1','1','1','1','1','1','1','1','0','1','1','1','1','1','0'),
        Array('1','0','1','1','1','0','0','1','1','0','1','1','1','1','1','1','1','1','1','1'),
        Array('1','1','1','1','0','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1'),
        Array('1','0','0','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1'),
        Array('1','0','1','1','1','1','1','1','0','1','1','1','0','1','1','1','0','1','1','1'),
        Array('0','1','1','1','1','1','1','1','1','1','1','1','0','1','1','0','1','1','1','1'),
        Array('1','1','1','1','1','1','1','1','1','1','1','1','0','1','1','1','1','0','1','1'),
        Array('1','1','1','1','1','1','1','1','1','1','0','1','1','1','1','1','1','1','1','1'),
        Array('1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1'),
        Array('0','1','1','1','1','1','1','1','0','1','1','1','1','1','1','1','1','1','1','1'),
        Array('1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1'),
        Array('1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1'),
        Array('1','1','1','1','1','0','1','1','1','1','1','1','1','0','1','1','1','1','1','1'),
        Array('1','0','1','1','1','1','1','0','1','1','1','0','1','1','1','1','0','1','1','1'),
        Array('1','1','1','1','1','1','1','1','1','1','1','1','0','1','1','1','1','1','1','0'),
        //Array('1','1','1','1','1','1','1','1','1','1','1','1','1','0','1','1','1','1','0','0'),
        //Array('1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1'),
        //Array('1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1'),
        Array('1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1','1')
    )))
  

【问题讨论】:

诚实的反馈,LeetCode 不是学习 Scala 的好资源。他们所有的问题都打算使用非常命令式的算法来解决,这只会教你语言的基本语法。您不会了解使 Scala 成为现在这样的所有东西,例如 Option 丰富的集合框架,EitherTry,更喜欢不变性,强类型系统等 虽然我倾向于同意 Luis Miguel 关于 LeetCode 倾向于命令式代码的观点,但并非总是如此。我用 15 行纯 FP Scala 解决了这个问题。 @jwvh 看到这个解决方案实际上会很有趣,至少作为一个 Sctie :) @LuisMiguelMejíaSuárez; As you wish. 【参考方案1】:

您的队列中会出现重复项,它们会滚雪球。将内容放入队列时,通过将队列更改为 Set 来检查它是否已经在其中。

例如,让我们检查一张地图:

1 1 1 1 1
1 1 1 1 1
1 1 1 1 1
1 1 1 1 1

最初,getPosition() 将返回 (0, 0)。

0, 0:您将放入队列:(0, 1) 和 (1, 0)。 0, 1:您将放入队列:(0, 2) 和 (1, 1)。队列现在有:(1, 0), (0, 2), (1, 1) 1, 0:您将放入队列:(2, 0) 和 (1, 1)。队列现在有:(0, 2), (1, 1), (2, 0), (1, 1)

请注意,队列中现在有 2 x (1, 1)。因此 (1, 1) 将被处理两次,因此将 2 x 结果添加到队列中(即 2 x (2, 1) 和 2 x (1, 2))。这些结果将复制成 4 x (2, 2)。因此会产生滚雪球效应。

这将是消耗队列中内存的雪球效应。如果使用 Set,则不会出现滚雪球效果,因为不会再次添加相同的位置。

另一种解决方案是在添加到队列时使用您的标记,而不是在处理如下代码时使用:

val queue = scala.collection.mutable.Queue[(Int, Int)]()
queue.enqueue((y,x))
grid(y)(x) = 'c'; // <--- moved to
while (queue.nonEmpty) 
    val me = queue.dequeue()
    val (me_y, me_x) = me
//  grid(me_y)(me_x) = 'c' // <--- moved from
    for (d <- dirs) 
        val (ny, nx) = (me_y + d(0), me_x+d(1))
        if (ny >= 0 && ny < grid.length
        && nx >= 0 && nx < grid(0).length
        && grid(ny)(nx) == '1') 
            queue.enqueue((ny, nx))
            grid(ny)(nx) = 'c'; // <--- moved to
        
    

因为你做了grid(ny)(nx) == '1'检查,同一个位置不会被多次添加到队列中。

【讨论】:

以上是关于(Scala) Leetcode 200. 超出内存限制的主要内容,如果未能解决你的问题,请参考以下文章

使用 scala 和 android-plugin 的 Proguard:java.lang.StringIndexOutOfBoundsException:字符串索引超出范围:160

scala中计算的的一个小问题,超出Int.maxValue时不会报错

当鼠标放在小图上显示大图时,显示的大图不能超出当前窗口?用js做吗?怎么做的?

scala

scala

LeetCode two sum -- scala 题解与思路