Scala代码中的性能问题-O(nlgn)比O(n)更快

时间:2019-04-20 03:42:56

标签: algorithm scala functional-programming

我刚开始使用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)
}

结果是,第一个版本的运行速度比第二个版本快。现在,我对自己可能出错的地方感到非常困惑。关于第二版中的递归,是否有导致高开销的事情?

我还想就编写第二个解决方案的惯用方法提出一些建议?这是我对函数式编程的第一次认真尝试,而且我正努力以尾递归的方式编写函数。

1 个答案:

答案 0 :(得分:1)

Vectorsignificantly slower than Arrays overall。特别是

  

Vector的逐项构造比Listmutable.Buffer的构造慢5-15倍,甚至比预先分配Array的构造慢40倍在可能的地方。

已知mapsorted数组的长度,因此可以预先分配它们。

正如该页面所述,Vector#:+确实是对数的,因此在两种情况下都以O(n log n)结尾。即使您没有这么做,O(n log n)O(n)从技术上也只能说出有关输入无限增长时性能如何变化的信息。多数情况下,它也与更快的速度相吻合,但大多数情况下都是如此。

  

我还想就编写第二个解决方案的惯用方式提出一些建议?

一种方法是按照相反的顺序构造List,然后在最后将其反转。或使用ArrayBuffer:即使它是可变的,您也可以有效地忽略它,因为您无需在任何地方保留对较早状态的引用。