我知道,SO不是作业的场所,因此,它非常针对问题范围。
我正在尝试在HackerRank:Array Manipulation - Crush上解决此问题。问题陈述非常简单,我实现了以下代码:
function arrayManipulation(n, queries) {
const arr = new Array(n).fill(0)
for (let j = 0; j < queries.length; j++) {
const query = queries[j];
const i = query[0] - 1;
const limit = query[1];
const value = query[2];
while (i < limit) {
arr[i++] += value;
}
}
return Math.max.apply(null, arr);
}
现在,它可以在一半的测试用例中正常工作,但会中断以下消息:由于超时7到13个测试用例,由于超时而终止,因为时间限制为 1秒 em>。
所以问题是,我可以在哪些方面改进此代码。根据我的理解,使用当前的算法并没有太多的范围(我可能是错的),那么如何改进算法呢?
注意:不使用.map
之类的数组函数来寻找替代项,因为.reduce
更快。另外,使用for
可以更快地进行自定义循环。为其附加参考。
参考:
答案 0 :(得分:2)
我们可以对此问题进行一些观察
a
和(b -k)的运行总和中表示从位置b
处的总和中减去k。时间复杂度O(q log q)与q是查询量。
示例:
a b k
1 5 3
4 8 7
6 9 1
我们将其分为
(1 3) (5 -3) (4 7) (8 -7) (6 1) (9 -1)
对它们进行排序:
(1 3) (4 7) (5 -3) (6 1) (8 -7) (9 -1)
然后一个接一个地进行:
Start sum = 0
-> (1 3) -> sum = 3
-> (4 7) -> sum = 10
-> (5 -3) -> sum = 7
-> (6 1) -> sum = 8
-> (8 -7) -> sum = 1
-> (9 -1) -> sum = 0
最大和为10->问题的答案。
我的Java代码通过了所有测试https://ideone.com/jNbKHa
答案 1 :(得分:1)
此算法会有所帮助。
https://www.geeksforgeeks.org/difference-array-range-update-query-o1/
使用此算法,您可以解决O(n+q)
和n = size of the array
中q = no of queries
的问题。
答案 2 :(得分:1)
为什么您的暴力解决方案无法通过所有测试用例?
今天的生成系统可以在一秒钟内执行10 ^ 8操作。请记住,在最坏的情况下,每个查询必须处理N = 10 ^ 7个输入。当您使用两个嵌套的for循环(一个用于添加K元素,另一个用于处理m个查询)时,解决方案的复杂度为O(NM)。
如果您使用O(NM)复杂性的解决方案,则必须处理(10 ^ 7 * 10 ^ 5)= 10 ^ 12的操作(在更坏的情况下(根本无法在1秒内计算出))
这就是为什么您会因蛮力解决方案而遇到超时错误的原因。 因此,您需要优化代码,这可以借助前缀求和数组来完成。
不是将k加到数组中从a到b的范围内的所有元素上,而是将差值数组累加
每当我们在数组的任何索引处添加任何内容并应用前缀求和算法时,都会将同一元素添加到每个元素,直到数组结尾。
ex- n = 5,m = 1,a = 2 b = 5 k = 5
i 0.....1.....2.....3.....4.....5.....6 //take array of size N+2 to avoid index out of bound
A[i] 0 0 0 0 0 0 0
将k = 5添加到a = 2
A [a] = A [a] + k //从应该添加k元素的位置开始索引
i 0.....1.....2.....3.....4.....5.....6
A[i] 0 0 5 0 0 0 0
现在应用前缀和算法
i 0.....1.....2.....3.....4.....5.....6
A[i] 0 0 5 5 5 5 5
因此您可以看到K = 5在应用前缀sum之后添加到所有元素,直到末尾,但是我们不必在末尾添加k。因此,要取消此效果,我们还必须在b + 1索引之后添加-K,以便仅在[a,b]范围内才会有K个元素的添加效果。
A [b + 1] = A [b] -k //删除第b个索引之后先前添加的k元素的影响。 这就是为什么在初始数组中将-k与+ k一起添加。
i 0.....1.....2.....3.....4.....5.....6
A[i] 0 0 5 0 0 0 -5
现在应用前缀和数组
i 0.....1.....2.....3.....4.....5.....6
A[i] 0 0 5 5 5 5 0
您现在可以看到,将K = 5从a = 2添加到b = 5,这是预期的。 在这里,我们只为每个查询更新两个索引,因此复杂度将为O(1)。
现在在输入中应用相同的算法
# 0.....1.....2.....3.....4.....5.....6 //taken array of size N+2 to avoid index out of bound
5 3 # 0 0 0 0 0 0 0
1 2 100 # 0 100 0 -100 0 0 0
2 5 100 # 0 100 100 -100 0 0 -100
3 4 100 # 0 100 100 0 0 -100 -100
要计算最大前缀和,请在获取最大累积前缀的同时将差分数组累加到。
执行所有操作后,现在应用前缀求和数组
i 0.....1.....2.....3.....4.....5.....6
A[i] 0 100 200 200 200 100 0
现在您可以遍历此数组以找到最大为200的数组。 遍历数组将花费O(N)时间,而更新每个查询的两个索引将花费O(1)*查询数量(m)
总体复杂度= O(N)+ O(M) = O(N + M)
这意味着=(10 ^ 7 + 10 ^ 5)小于10 ^ 8(每秒)
注意:如果要搜索 video tutorial ,则必须将其签出 here 以获得详细说明。< / p>
答案 3 :(得分:0)
我认为诀窍不是真正对数组执行操作。
您可以简单地跟踪索引间隔的变化。
保留间隔的排序列表(按begin-index排序)。
$ mv $HOME/.docker to_something_else
在最后一步中,您要遍历间隔并取最大值。