如何找到序列的第二个最小元素

时间:2019-07-06 21:23:14

标签: scala

我找不到有关Scala的任何文档,但是对于从未使用或很少使用递归的其他编程语言,却找不到很多文档。

应该允许该序列为空并且包含双精度数。

val nonEmpthySeq = Seq(1,2,3,4,4,4,5,6,7,7,67,8,9,9,10)
val oneElementSeq = Seq(4)
val empthySeq = Seq()

我尝试过的事情:

我无法为此回答,因为我的问题可能是重复的。

使用模式匹配

def secondSmallest(xs: Seq[Int]): Option[Int] = xs match {
  case Nil => None
  case `xs` => Some(xs.distinct.sorted.apply(1))
}

超级干净的单缸纸

def secondSmallest(xs: Seq[Int]): Option[Int] =
    Try(xs.distinct.sorted.apply(1)).toOption

两个都返回

secondSmallest(nonEmpthySeq)
secondSmallest(oneElementSeq)
secondSmallest(empthySeq)
res0: Option[Int] = Some(2)
res1: Option[Int] = None
res2: Option[Int] = None

res1解释:

x::Nil,对于Seq(4)中的secondSmallest(oneElementSeq)必须为None,因为逻辑上列表中没有第二高元素,因此它必须为None

如果只需要一个元素,则必须使用case x :: Nil => Some(x)处理它。

def secondSmallest(xs: Seq[Int]): Option[Int] = xs match {
  case Nil => None
  case x :: Nil => Some(x)
  case `xs` => Some(xs.distinct.sorted.apply(1))
}

3 个答案:

答案 0 :(得分:3)

又快又脏

list.distinct.sorted.lift(1)

lift部分处理的情况是位置1上没有条目。


适当的线性解

此方法在O(n)中有效,只扫描一次列表:

def secondLargest[A: Ordering](as: Seq[A]): Option[A] = {
    val bottom = Option.empty[A]
    val ord = implicitly[Ordering[Option[A]]]
    import ord._
    as.foldLeft((bottom, bottom)) {
      case ((largest, second), x) =>
        val wrapped = Some(x)
        if (wrapped > largest) (wrapped, largest)
        else if (wrapped > second) (largest, wrapped)
        else (largest, second)
    }._2
}

在扫描序列时,它会保留两个Option[A],其中两个元素最大。对Option[A]进行比较的原因在于,Ordering提供了一个隐式形式,该隐式形式将None作为底部元素与类型A上的任何排序(这是ord )。

示例:

println(secondLargest(Seq("foo", "bar", "baz")))
println(secondLargest(Seq(4, 7, 2, 5, 9, 6, 4)))

打印:

// Some(baz)
// Some(7)

请注意,所有基于热切排序的解决方案都至少为O(n*log(n)),这是不好的,因为存在一种快速选择算法,可以在预期的线性时间内找到k最大的元素。


修改

哦,好吧...如果要第二个最小,请颠倒顺序:

def secondSmallest[A: Ordering](as: Seq[A]): Option[A] =
  secondLargest(as)(implicitly[Ordering[A]].reverse)

println(secondSmallest(Seq("aa", "ca", "ab", "bc", "cc"))) // Some(ab)
println(secondSmallest(Seq(4, 7, 2, 5, 9, 6, 4)))          // Some(4)

答案 1 :(得分:2)

警告:在遵循此答案之前,请注意安德烈的评论。


这是我在评论中从蒂姆偷来的一个解决方案:

def secondHighest(xs:Seq[Int]): Option[Int] = {
  xs.distinct.sorted.init.lastOption
}

def secondLowest(xs:Seq[Int]): Option[Int] = {
  xs.distinct.sorted.drop(1).headOption
}

我自己的误导尝试是

Try(xs.distinct.sorted.apply(1)).toOption

答案 2 :(得分:1)

PriorityQueue ScalaDocs页上:

  

此类使用堆来实现优先级队列。

从堆数据结构上的Wikipedia page

  

Heap数据结构可用于有效地找到数组中第k个最小(或最大)的元素。

在这种情况下,也许我们可以在不牺牲太多效率的情况下推广问题。

def fromTop[A:Ordering](xs :Seq[A], offset :Int) :Option[A] = {
  val pq = collection.mutable.PriorityQueue(xs:_*)
  for (_ <- 1 until offset) {
    val init = pq.headOption
    while (pq.nonEmpty && pq.head == init.get)
      pq.dequeue()
  }
  pq.headOption
}

def fromBottom[A:Ordering](xs :Seq[A], offset :Int) :Option[A] =
  fromTop(xs, offset)(implicitly[Ordering[A]].reverse)

测试:

fromTop(Vector.empty[Int], 2)            //res0: Option[Int] = None
fromTop(Vector(9, 88), 0)                //res1: Option[Int] = Some(88)
fromTop(Vector('a','c','t','u','p'), 3)  //res2: Option[Char] = Some(p)

fromBottom(List(true,false,true), 2)     //res3: Option[Boolean] = Some(true)
fromBottom(List(1,2,3), 4)               //res4: Option[Int] = None
fromBottom(List(1,1,1,2,2,2,3,3,3), 2)   //res5: Option[Int] = Some(2)