Быстро и грязно
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)