在scala中以递归方式查找列表中的最大值

时间:2016-06-26 22:01:30

标签: scala recursion

我是Scala的新手,有更好的方法可以用最基本的知识表达这一点吗?

 def findMax(xs: List[Int]): Int = {
      xs match {
        case x :: tail => (if (tail.length==0) x else (if(x>findMax(tail)) x else (findMax(tail))))
      }
    }

6 个答案:

答案 0 :(得分:10)

这是两个问题。首先,你调用tail.length这是一个O(N)的操作,所以在最坏的情况下,这将花费你N * N步,其中N是序列的长度。第二个是你的函数不是尾递归 - 你将findMax调用从外部嵌入到内部“。

编写正确的递归函数的通常策略是

  • 考虑每种可能的模式案例:这里有空列表Nil或非空列表head :: tail。这解决了您的第一个问题。
  • 携带临时结果(这里是最大值的当前猜测)作为函数的另一个参数。这解决了你的第二个问题。

这给出了:

import scala.annotation.tailrec

@tailrec
def findMax(xs: List[Int], max: Int): Int = xs match {
  case head :: tail => findMax(tail, if (head > max) head else max)
  case Nil => max
}

val z = util.Random.shuffle(1 to 100 toList)
assert(findMax(z, Int.MinValue) == 100)

如果您不想公开这个附加参数,可以编写一个辅助内部函数。

def findMax(xs: List[Int]): Int = {
  @tailrec
  def loop(ys: List[Int], max: Int): Int = ys match {
    case head :: tail => loop(tail, if (head > max) head else max)
    case Nil => max
  }
  loop(xs, Int.MinValue)
}

val z = util.Random.shuffle(1 to 100 toList)
assert(findMax(z) == 100)

为简单起见,如果列表为空,则返回Int.MinValue。更好的解决方案可能是为这种情况抛出异常。

这里的@tailrec注释是可选的,它只是确保我们确实定义了一个尾递归函数。这样做的好处是,如果列表非常长,我们就不会产生堆栈溢出。

答案 1 :(得分:5)

每当您将集合缩减为单个值时,请考虑使用fold函数之一而不是显式递归。

List(3,7,1).fold(Int.MinValue)(Math.max)
// 7

答案 2 :(得分:2)

即使我也是Scala的新手(尽管我已经进入了Haskell!)。

我对此的尝试如下:

请注意,我假设一个非空列表,因为空列表的最大值没有意义。

我首先定义一个辅助方法,它只返回最多2个数字。

def maxOf2 (x:Int, y:Int): Int = {
   if (x >= y) x
   else y
}

有了这个简单的功能,我们可以建立一个递归函数来找到最大值的' max'如下:

def findMax(xs: List[Int]): Int = {
    if (xs.tail.isEmpty)
        xs.head
    else
        maxOf2(xs.head, findMax(xs.tail))
}

我认为这是一个非常明确的方式(虽然不是高效的#);这样做。

我想让递归的概念变得明显。

希望这有帮助!

答案 3 :(得分:1)

详细说明@ fritz的答案。如果您传递一个空列表,它将抛出java.lang.UnsupportedOperationException:空列表的尾巴

因此,在保持算法完好无损的情况下,我进行了以下调整:

def max(xs: List[Int]): Int = {
  def maxOfTwo(x: Int, y: Int): Int = {
    if (x >= y) x else y
  }
  if (xs.isEmpty) throw new UnsupportedOperationException("What man?")
  else if (xs.size == 1) xs.head
  else maxOfTwo(xs.head, max(xs.tail))
}

@ fritz谢谢您的回答

答案 4 :(得分:0)

使用模式匹配递归,

def top(xs: List[Int]): Int = xs match {
  case Nil      => sys.error("no max in empty list")
  case x :: Nil => x
  case x :: xs  => math.max(x, top(xs))
}

模式匹配用于将列表分解为头部和休息。单个元素列表用x :: Nil表示。我们递归列表的其余部分,并在每个递归阶段比较列表的head项目的最大值。为了使案例详尽无遗(以制定明确定义的函数),我们还考虑空列表(Nil)。

答案 5 :(得分:0)

def maxl(xl: List[Int]): Int = {
  if ( (xl.head > xl.tail.head) && (xl.tail.length >= 1) )
    return xl.head
  else
    if(xl.tail.length == 1)
      xl.tail.head
    else
      maxl(xl.tail)
}