快速询问有关大输入数据的高阶快速函数的效率。在最近的测试中,我有一个关于找到equlibirum索引的问题。在数组中 - 即数组的索引,其中索引之下的所有元素的总和等于索引之上的所有元素的总和
该阵列的平衡指数是任何整数P,使得0≤P<1。 N和较低指数的元素之和等于 更高指数的要素,即
A[0] + A[1] + ... + A[P−1] = A[P+1] + ... + A[N−2] + A[N−1].
挑战在于编写一个简短的函数来计算第一个(或任何)被认为是“平衡”的指数。 我把一个简单的片段放在一起,得分很高,但却失败了一些表演&#39;使用大量输入数据的测试(数组大小约为100,000)。
这是代码
public func solution(inout A : [Int]) -> Int {
var index = 0;
for _ in A {
let sumBefore = A[0...index].reduce(0) { $0 + $1 }
let sumAfter = A[index...A.count-1].reduce(0) { $0 + $1 }
if (sumBefore == sumAfter) { return index; }
index += 1;
}
return -1;
}
是否有人能够解释为什么代码在大型数据集或任何推荐的替代方案中表现如此糟糕?
这里,例如是失败性能测试的描述:
大型性能测试,O(n ^ 2)解决方案应该失败。
✘超时错误 运行时间:&gt; 6.00秒,时间限制:0.22秒
答案 0 :(得分:4)
问题不在于#34;内置功能的表现非常差。&#34;
您的解决方案很慢,因为在每次迭代中,N
个元素都是
添加(N
是数组的长度)。它会更有效率
计算总和一次并在总和之前更新&#34;&#34;
和&#34;总和后#34;穿过阵列时。这减少了
从O(N^2)
到O(N)
的复杂性:
public func solution(A : [Int]) -> Int {
var sumBefore = 0
var sumAfter = A.reduce(0, combine: +)
for (idx, elem) in A.enumerate() {
sumAfter -= elem
if sumBefore == sumAfter {
return idx
}
sumBefore += elem
}
return -1
}
(Swift 2.2,Xcode 7.3.1)
说明:
inout
参数传递。+
)可以作为参数传递给reduce()
函数。enumerate()
返回一系列数组索引
相应的元素,这节省了另一个数组访问。还要注意更多&#34; Swifty&#34;设计将是返回类型
如果找不到解决方案,则Int?
为nil
。
答案 1 :(得分:4)
看起来挑战失败了,因为您的解决方案是O(n^2)
。
您的for
循环以及内部的2个连续reduce
会使您的解决方案变为〜O(2*n^2)
,因为reduce
会再次遍历所有元素。
更简单的解决方案是首先计算整个和,然后迭代元素一次,逐个减去整个和中的每个值,从而可以访问左右和,以进行比较。
使用Swift 3.0,Xcode 8:
func findEquilibriumIndex(in array: [Int]) -> Int? {
var leftSum = 0
var rightSum = array.reduce(0, combine: +)
for (index, value) in array.enumerated() {
rightSum -= value
if leftSum == rightSum {
return index
}
leftSum += value
}
return nil
}
let sampleArray = [-7, 1, 5, 2, -4, 3, 0]
findEquilibriumIndex(in: sampleArray)
答案 2 :(得分:0)
如果您定义此扩展程序
extension Array where Element : SignedInteger {
var incrementalSums: [Element] {
return Array(reduce([0]) { $0.0 + [$0.0.last! + $0.1] }.dropLast())
}
}
给定一个Int(s)
数组,您可以构建一个数组,其中Int
位置的n-th
代表从0
到(n-1)
的值的总和在原始数组中。
实施例
[1, 2, 3, 10, 2].incrementalSums // [0, 1, 3, 6, 16]
现在你可以构建一个像这样的函数
func equilibriumIndex(nums: [Int]) -> Int? {
let leftSums = nums.incrementalSums
let rightSums = nums.reversed().incrementalSums.reversed()
return Array(zip(leftSums, rightSums)).index { $0 == $1 }
}
答案 3 :(得分:0)
以下是Swift 3中解决方案的功能版本
let total = sampleArray.reduce(0,+)
var sum = 0
let index = sampleArray.index{ v in defer {sum += v}; return sum * 2 == total - v }
如果我理解正确,结果索引中的元素被排除在每一方的总和之外(我不确定其他解决方案是否实现)