我刚开始使用Scala。我正在尝试通过解决leetcode上的简单问题来学习它。这是我在LC #977上的第一次(成功)尝试:
def sortedSquares(A: Array[Int]): Array[Int] = {
A.map(math.abs).map(x => x * x).sorted
}
由于排序,我希望它运行O(NlgN)
时间,N
是输入数组的大小。但是我知道有一个针对此问题的两点解决方案,其运行时复杂度为O(N)
。因此,我继续在我的新手Scala中实现了这一点:
def sortedSquaresHelper(A: Array[Int], result: Vector[Int], left: Int, right: Int): Array[Int] = {
if (left < 0 && right >= A.size) {
result.toArray
} else if (left < 0) {
sortedSquaresHelper(A, result :+ A(right) * A(right), left, right + 1)
} else if (right >= A.size) {
sortedSquaresHelper(A, result :+ A(left) * A(left), left - 1, right)
} else {
if (math.abs(A(left)) < math.abs(A(right))) {
sortedSquaresHelper(A, result :+ A(left) * A(left), left - 1, right)
} else {
sortedSquaresHelper(A, result :+ A(right) * A(right), left, right + 1)
}
}
}
def sortedSquares(A: Array[Int]): Array[Int] = {
val min_idx = A.zipWithIndex.reduceLeft((x, y) => if (math.abs(x._1) < math.abs(y._1)) x else y)._2
val result: Vector[Int] = Vector(A(min_idx) * A(min_idx))
sortedSquaresHelper(A, result, min_idx - 1, min_idx + 1)
}
结果是,第一个版本的运行速度比第二个版本快。现在,我对自己可能出错的地方感到非常困惑。关于第二版中的递归,是否有导致高开销的事情?
我还想就编写第二个解决方案的惯用方法提出一些建议?这是我对函数式编程的第一次认真尝试,而且我正努力以尾递归的方式编写函数。
答案 0 :(得分:1)
Vector
是significantly slower than Array
s overall。特别是
Vector
的逐项构造比List
或mutable.Buffer
的构造慢5-15倍,甚至比预先分配Array
的构造慢40倍在可能的地方。
已知map
和sorted
数组的长度,因此可以预先分配它们。
正如该页面所述,Vector#:+
确实是对数的,因此在两种情况下都以O(n log n)
结尾。即使您没有这么做,O(n log n)
与O(n)
从技术上也只能说出有关输入无限增长时性能如何变化的信息。多数情况下,它也与更快的速度相吻合,但大多数情况下都是如此。
我还想就编写第二个解决方案的惯用方式提出一些建议?
一种方法是按照相反的顺序构造List
,然后在最后将其反转。或使用ArrayBuffer
:即使它是可变的,您也可以有效地忽略它,因为您无需在任何地方保留对较早状态的引用。