【问题标题】:Given an array of integers, return indices of the two numbers such that they add up to a specific target with Scala给定一个整数数组,返回两个数字的索引,使它们加起来为 Scala 的特定目标
【发布时间】:2020-05-06 23:09:43
【问题描述】:

我通过解决算法问题来练习 Scala,并通过 leetcode 解决了这个问题:

给定一个整数数组,返回两个数字的索引,例如 它们加起来就是一个特定的目标。您可以假设每个输入 将只有一个解决方案,并且您可能不会使用相同的元素 两次。

Example:

Given nums = [2, 7, 11, 15], target = 9,

Because nums[0] + nums[1] = 2 + 7 = 9,
return [0, 1].

我想出了以下代码

def twoSum(nums: Array[Int], target: Int): Array[Int] = {
    val map = scala.collection.mutable.Map.empty[Int, Int]
    nums.zipWithIndex.foreach{ case(num, idx) => 
        map.get(idx) match {
            case Some(r) => return Array(r, idx)
            case None => map += (idx -> (target-num))
       }
    }
    Array(-1, -1)
}

似乎我以错误的方式将值添加到 map,因为对于每个输入我都会得到一个 Array(-1,-1) 结果。

预期的灵魂。

def twoSum(nums: Array[Int], target: Int): Array[Int] = {
    var map = Map.empty[Int, Int]
    for ((v, i) <- nums.zipWithIndex) {
       map.get(v) match {
           case Some(r) => return Array(r, i)
           case None => map ++= Map((target - v) -> i)
       }
    }
    Array(-1, -1)
}

你能指出我的错误并解释如何正确更新 hashMap 吗?在使用可变 Map 更新元素时​​,我引用了这个 post

【问题讨论】:

  • 请注意:在这个例子中你完全没有理由使用可变性。也没有理由使用return

标签: scala


【解决方案1】:

在第二种解决方案中,您有一个索引值映射,这是正确的。但是在第一个中,你有相反的情况:从索引到值的映射,这是错误的。所以你必须反转你添加到Map的条目:

def twoSum(nums: Array[Int], target: Int): Array[Int] = {
  val map = scala.collection.mutable.Map.empty[Int, Int]
  nums.zipWithIndex.foreach{ case(num, idx) =>
    map.get(num) match {                     // Changed: retrieving the index of the value 
      case Some(r) => return Array(r, idx)
      case None => map += ((target-num) -> idx)  // Changed: adding (value -> index) pairs
    }
  }
  Array(-1, -1)
}

为了记录,如果你想避免使用可变性和return,最简单的方法是预先计算Map,然后在第二遍中找到两个索引:

import scala.collection.immutable.HashMap

def twoSum(nums: Array[Int], target: Int): Array[Int] = {
  val indexByValue = HashMap(nums.zipWithIndex: _*)
  val result = nums.indices.collectFirst(Function.unlift { i =>
    indexByValue.get(target - nums(i)).filter(_ != i).map(Array(i, _))
  })
  result.getOrElse(Array(-1, -1))
}

这仍然比可变版本慢,因为它两次传递 nums 并急切地构建整个 Map,而原始可变代码只执行一次传递并懒惰地构建 Map。要以函数方式一次通过惰性计算Map,您可以在迭代器或一些惰性集合上使用scanLeft

import scala.collection.immutable.HashMap

def twoSum(nums: Array[Int], target: Int): Array[Int] = {
  nums.indices.iterator
    .scanLeft(HashMap.empty[Int, Int]) { (map, i) =>
      map + (nums(i) -> i)
    }
    .zipWithIndex
    .collectFirst(Function.unlift { case (indexByValue, i) =>
      indexByValue.get(target - nums(i)).filter(_ != i).map(Array(i, _))
    })
    .getOrElse(Array(-1, -1))
}

【讨论】:

    【解决方案2】:

    您可以模拟在命令式语言(嵌套 for)上执行的操作。
    (注意:我将签名更改为更惯用,您可以在原始语言上调用此函数,调整输入和输出)

    import scala.collection.immutable.ArraySeq // Immutable array, only exists on 2.13
    
    def twoSum(nums: ArraySeq[Int], target: Int): Option[(Int, Int)] =
      Iterator
        .range(start = 0, end = nums.length)
        .map { i =>
          Iterator
            .range(start = (i + 1), end = nums.length)
            .collectFirst {
              case j if ((nums(i) + nums(j)) == target) =>
                (i, j)
            }
        } collectFirst {
          case Some(tuple) => tuple
        }
    

    你可以像这样使用:

    twoSum(ArraySeq(1, 3, 5, 2, 0, 4), target = 8)
    // res: Option[(Int, Int)] = Some((1,2))
    
    twoSum(ArraySeq(1, 3, 5, 2, 0, 4), target = 10)
    // res: Option[(Int, Int)] = None
    

    【讨论】:

      猜你喜欢
      • 2019-12-23
      • 2019-03-30
      • 2018-03-09
      • 1970-01-01
      • 2014-02-18
      • 1970-01-01
      • 2014-02-09
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多