如何递归地找到整数列表中的最大元素?

时间:2013-09-27 06:31:35

标签: list scala max

我正在尝试编写一个函数,它将以递归方式查找整数列表中的最大元素。我知道如何在Java中执行此操作,但无法理解如何在Scala中执行此操作。

这是我到目前为止所做的,但没有递归:

  def max(xs: List[Int]): Int = {
    if (xs.isEmpty) throw new java.util.NoSuchElementException();
    else xs.max;
  }

我们如何使用Scala语义递归地找到它。

16 个答案:

答案 0 :(得分:36)

这是我曾经能想到的最小的递归实现:

def max(xs: List[Int]): Option[Int] = xs match {
  case Nil => None
  case List(x: Int) => Some(x)
  case x :: y :: rest => max( (if (x > y) x else y) :: rest )
} 

它通过比较列表中的前两个元素,丢弃较小的(或第一个,如果两者都相等),然后在剩余列表上调用自身。最终,这会将列表缩减为一个必须最大的元素。

我返回一个选项来处理给出一个空列表而不抛出异常的情况 - 这会强制调用代码识别可能性并处理它(如果他们则由调用者处理)想要抛出异常)。

如果你想要它更通用,它应该写成这样:

def max[A <% Ordered[A]](xs: List[A]): Option[A] = xs match {
  case Nil => None
  case x :: Nil => Some(x)
  case x :: y :: rest => max( (if (x > y) x else y) :: rest )
}

哪个类型可以扩展Ordered特征,或者在范围内从A隐式转换为Ordered[A]。因此,默认情况下,它适用于IntBigIntCharString等等,因为scala.Predef为它们定义了转化。

我们可以变得更加通用:

def max[A <% Ordered[A]](xs: Seq[A]): Option[A] = xs match {
  case s if s.isEmpty || !s.hasDefiniteSize => None
  case s if s.size == 1 => Some(s(0))
  case s if s(0) <= s(1) => max(s drop 1)
  case s => max((s drop 1).updated(0, s(0)))
}

不仅可以使用列表,还可以使用向量和任何其他扩展Seq特征的集合。请注意,我必须添加一个检查以查看序列是否确实具有确定的大小 - 它可能是一个无限的流,所以如果可能是这种情况,我们会退回。如果您确定您的流将具有确定的大小,您可以在调用此函数之前始终强制它 - 无论如何它将在整个流中工作。最后请参阅注释,了解为什么我确实不希望返回None无限期流。我这里只是为了简单起见。

但这不适用于集合和地图。该怎么办?下一个常见的超类型是Iterable,但这不支持updated或任何等效的。我们构建的任何东西对于实际类型都可能表现不佳。所以我干净的无辅助函数递归会崩溃。我们可以改为使用辅助函数,但是在其他答案中有很多例子,我将坚持使用一个简单的函数方法。所以在这一点上,我们可以切换到reduceLeft(当我们在它的时候,让我们去'Traversable'并迎合所有集合):

def max[A <% Ordered[A]](xs: Traversable[A]): Option[A] = {
  if (xs.hasDefiniteSize) 
    xs reduceLeftOption({(b, a) => if (a >= b) a else b}) 
  else None
}

但是如果你不认为reduceLeft是递归的,我们可以这样做:

def max[A <% Ordered[A]](xs: Traversable[A]): Option[A] = xs match {
  case i if i.isEmpty => None
  case i if i.size == 1 => Some(i.head)
  case i if (i collect { case x if x > i.head => x }).isEmpty => Some(i.head)
  case _ => max(xs collect { case x if x > xs.head => x })
}

它使用collect组合器来避免一些笨拙的方法从xs.headxs drop 2中提取新的迭代器。

这些中的任何一个都可以安全地与任何有订单的任何集合一起使用。例子:

scala>  max(Map(1 -> "two", 3 -> "Nine", 8 -> "carrot"))
res1: Option[(Int, String)] = Some((8,carrot))

scala> max("Supercalifragilisticexpialidocious")
res2: Option[Char] = Some(x)

我通常不会将这些其他人作为示例,因为它需要更多关于Scala的专业知识。

另外,请记住基本Traversable特征提供了max方法,所以这只是为了练习;)

注意:我希望我的所有示例都显示如何仔细选择案例表达式的序列,使每个案例表达式尽可能简单。

更重要的注意事项:哦,同样,虽然我非常乐意回复None输入Nil,但实际上我非常倾向于抛出一个hasDefiniteSize == false的例外情况。首先,有限流可以具有明确或非确定的大小,纯粹依赖于评估序列,并且在这些情况下该函数将有效地随机返回Option - 这可能需要很长时间才能跟踪。其次,我希望人们能够区分已经通过Nil和通过真正的风险输入(即无限流)。我只在这些演示中返回Option,以使代码尽可能简单。

答案 1 :(得分:16)

最简单的方法是使用TraversableOnce特征的最大函数,如下所示,

val list = (1 to 10).toList
list.max

要防止空虚,你可以做这样的事情,

if(list.empty) None else Some(list.max)

上面会给你一个Option[Int]

我的第二种方法是使用foldLeft

(list foldLeft None)((o, i) => o.fold(Some(i))(j => Some(Math.max(i, j))))

或者如果你知道在空列表的情况下要返回的默认值,这将变得更加简单。

val default = 0
(list foldLeft default)(Math.max)

无论如何,因为你的要求是以递归方式进行,我建议关注,

def recur(list:List[Int], i:Option[Int] = None):Option[Int] = list match {
  case Nil => i
  case x :: xs => recur(xs, i.fold(Some(x))(j => Some(Math.max(j, x))))
}

或默认情况下,

val default = 0
def recur(list:List[Int], i:Int = default):Int = list match {
  case Nil => i
  case x :: xs => recur(xs, i.fold(x)(j => Math.max(j, x)))
}

请注意,这是tail recursive。因此堆栈也被保存。

答案 2 :(得分:13)

如果您想要解决此问题的功能方法,请使用reduceLeft

def max(xs: List[Int]) = {
  if (xs.isEmpty) throw new NoSuchElementException
  xs.reduceLeft((x, y) => if (x > y) x else y)
}

这个特定于整数列表的函数,如果你需要更通用的方法,那么使用Ordering类型类:

def max[A](xs: List[A])(implicit cmp: Ordering[A]): A = {
  if (xs.isEmpty) throw new NoSuchElementException
  xs.reduceLeft((x, y) => if (cmp.gteq(x, y)) x else y)
}   

reduceLeft是一个高阶函数,它接受(A, A) => A类型的函数,在这种情况下需要两个整数,比较它们并返回更大的一个。

答案 3 :(得分:8)

您可以使用类似

的模式匹配
def max(xs: List[Int]): Int = xs match {
  case Nil => throw new NoSuchElementException("The list is empty")
  case x :: Nil => x
  case x :: tail => x.max(max(tail)) //x.max is Integer's class method
}

答案 4 :(得分:6)

Scala是一种功能性语言,鼓励人们以递归方式思考。我的解决方案如下。我根据你给定的方法重复它。

  def max(xs: List[Int]): Int = {
    if(xs.isEmpty == true) 0
    else{
      val maxVal= max(xs.tail)
      if(maxVal >= xs.head) maxVal 
      else                  xs.head     
    }
  }

通过建议更新了我的尾递归解决方案。

  def max(xs: List[Int]): Int = {    
    def _max(xs: List[Int], maxNum: Int): Int = {   
      if (xs.isEmpty) maxNum
      else {
        val max = {
          if (maxNum >= xs.head) maxNum
          else xs.head
        }
        _max(xs.tail, max)
      }
    }
    _max(xs.tail, xs.head)
  }

答案 5 :(得分:1)

我只使用head()和tail()

def max(xs: List[Int]): Int = {
    if (xs.isEmpty) throw new NoSuchElementException
    else maxRecursive(xs.tail,xs.head) 
  }

  def maxRecursive(xs: List[Int], largest: Int): Int = {
    if (!xs.isEmpty ){
      if (xs.head > largest) maxRecursive(xs.tail, xs.head)
      else maxRecursive(xs.tail, largest)
    }else{
      largest
    }
  }

这是测试:

test("max of a few numbers") {
    assert(max(List(3, 7, 2, 1, 10)) === 10)
    assert(max(List(3, -7, 2, -1, -10)) === 3)
    assert(max(List(-3, -7, -2, -5, -10)) === -2)
  }

答案 6 :(得分:0)

  1. 折叠可以提供帮助:

    if(xs.isEmpty)
      throw new NoSuchElementException
    else
      (Int.MinValue /: xs)((max, value) => math.max(max, value))
    
  2. 列表和模式匹配(已更新,感谢@ x3ro)

    def max(xs:List[Int], defaultValue: =>Int):Int = {
      @tailrec
      def max0(xs:List[Int], maxSoFar:Int):Int = xs match {
        case Nil => maxSoFar
        case head::tail => max0(tail, math.max(maxSoFar, head))
      }
      if(xs.isEmpty)
        defaultValue
      else
        max0(xs, Int.MinValue)
    }
    
  3. (此解决方案不会每次都创建Option实例。它也是尾递归的,并且与命令式解决方案一样快。)

答案 7 :(得分:0)

看起来你刚刚开始使用scala,所以我尝试给你最简单的答案,如何递归:

 def max(xs: List[Int]): Int = {
   def maxrec(currentMax : Int, l: List[Int]): Int = l match {
     case Nil => currentMax
     case head::tail => maxrec(head.max(currentMax), tail) //get max of head and curretn max
   }
   maxrec(xs.head, xs)
 }

此方法定义了一个自己的内部方法(maxrec)来处理递归。它会失败(例外)你给它一个空列表(空列表上没有最大值)

答案 8 :(得分:0)

这是我的代码(我是函数式编程的新手),我假设在这个问题下出现的人都会像我一样。最好的答案,虽然很棒,对于新手来说有点太多了!所以,这是我简单的答案。请注意,我被要求(作为课程的一部分)仅使用头部和尾部进行此操作。

/**
   * This method returns the largest element in a list of integers. If the
   * list `xs` is empty it throws a `java.util.NoSuchElementException`.
   *
   * @param xs A list of natural numbers
   * @return The largest element in `xs`
   * @throws java.util.NoSuchElementException if `xs` is an empty list
   */
    @throws(classOf[java.util.NoSuchElementException])
    def max(xs: List[Int]): Int = find_max(xs.head, xs.tail)

    def find_max(max: Int, xs: List[Int]): Int = if (xs.isEmpty) max else if (max >= xs.head) find_max(max, xs.tail) else find_max(xs.head, xs.tail)

一些测试:

test("max of a few numbers") {
    assert(max(List(3, 7, 2)) === 7)
    intercept[NoSuchElementException] {
      max(List())
    }
    assert(max(List(31,2,3,-31,1,2,-1,0,24,1,21,22)) === 31)
    assert(max(List(2,31,3,-31,1,2,-1,0,24,1,21,22)) === 31)
    assert(max(List(2,3,-31,1,2,-1,0,24,1,21,22,31)) === 31)
    assert(max(List(Int.MaxValue,2,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,222,3,-31,1,2,-1,0,24,1,21,22)) === Int.MaxValue)
  }

答案 9 :(得分:0)

list.sortWith(_&gt; )。head&amp; list.sortWith(&gt; _)。reverse.head表示最大和最小的数字

答案 10 :(得分:0)

如果你需要使用isEmpty在head列表中编写递归max函数,head和tail并为空列表抛出异常:

def max(xs: List[Int]): Int =
  if (xs.isEmpty) throw new NoSuchElementException("max of empty list")
  else if (xs.tail.isEmpty) xs.head
  else if (xs.head > xs.tail.head) max(xs.head :: xs.tail.tail)
  else max(xs.tail)

如果您在列表中使用max函数,则它很简单(您不需要编写自己的递归函数):

val maxInt = List(1, 2, 3, 4).max

答案 11 :(得分:0)

def max(xs: List[Int]): Int = {
  def _max(xs: List[Int], maxAcc:Int): Int = {
    if ( xs.isEmpty ) 
      maxAcc 
    else 
      _max( xs.tail, Math.max( maxAcc, xs.head ) ) // tail call recursive
  }

  if ( xs.isEmpty ) 
    throw new NoSuchElementException() 
  else 
    _max( xs, Int.MinValue );
}

答案 12 :(得分:0)

具有尾递归功能

  @tailrec
  def findMax(x: List[Int]):Int =  x match {
    case a :: Nil => a
    case a :: b :: c =>  findMax( (if (a > b) a else b) ::c)
  }

答案 13 :(得分:0)

具有模式匹配功能以查找最大值,如果为空则返回零

  def findMax(list: List[Int]) = {
    def max(list: List[Int], n: Int) : Int = list match {
      case h :: t => max(t, if(h > n) h else n)
      case _ => n
    }
    max(list,0)
  }

答案 14 :(得分:0)

我想这是为progfun示例

这是我能想到的最简单的递归解决方案

  def max(xs: List[Int]): Int = {
    if (xs.isEmpty) throw new NoSuchElementException("The list is empty")
    val tail = xs.tail
    if (!tail.isEmpty) maxOfTwo(xs.head, max(xs.tail))
    else xs.head
  }

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

答案 15 :(得分:-2)

 def max(xs: List[Int]): Int = xs match {
    case Nil => throw new NoSuchElementException("empty list!")
    case x :: Nil => x
    case x :: tail => if (x > max(tail)) x else max(tail)
  }