给定一个整数数组(正数和负数),每个数组最多有K位(加上符号位),并且已知数组中所有整数的总和也至多有K位(加上标志位)。设计一种计算数组中整数之和的算法,所有中间和也最多具有K位(加上符号位)。 [提示:找到你应该添加正数和负数的顺序]。
这是面试材料而不是作业的问题
我实际上正在考虑创建两个单独的数组,一个用于正数,另一个用于负数,对它们进行排序,然后添加两个数据,以便大多数负数被添加到大多数正数...但这似乎有O(nlogn)时间复杂度(排序)和O(n)空间复杂度>请帮忙!
答案 0 :(得分:7)
首先请注意,即使你让立即结果溢出,如果可以表示最终结果也总是正确的。这是因为任何大小的整数类型都像大多数语言中的循环组一样,包括Java(不是在C中,整数溢出是未定义的行为,C#,它能够引发溢出异常)。
如果仍想要防止溢出,请按照以下方式执行此操作:
将数组就地分割为其负数条目(按任意顺序)和正数条目(按任意顺序)。零可以在任何地方结束。换句话说,执行一个快速排序步骤,枢轴为零。
让ni
指向数组的开头(负数条目所在的位置)。
pi
指向数组的末尾。sum
为零。 pi >= ni
sum
为否定
arr[pi]
添加到sum
。arr[pi]
为否定(我们已用完正加数)并且sum
为正(出现溢出),则结果会溢出。pi
arr[ni]
添加到sum
。arr[ni]
为正数且sum
为负数,则结果会溢出。ni
。最后,检查sum
是否超过K
位。如果是,则声明结果溢出。
答案 1 :(得分:2)
主要思想是使用两个索引迭代数组:正面和负面元素。当总和为负时,我们将搜索下一个正元素(使用相应的迭代器)以添加到总和,否则 - 添加到负数。
此代码应该有效:
public final class ArrayAdder {
@NotNull
private final int[] array;
private int sum;
public ArrayAdder(@NotNull int[] array) {
this.array = array;
}
public int sum() {
sum = 0;
final IntPredicate positive = v -> v > 0;
final Index positiveIndex = new Index(positive);
final Index negativeIndex = new Index(positive.negate());
while (positiveIndex.index < array.length || negativeIndex.index < array.length) {
sum += sum < 0 ? sum(positiveIndex, negativeIndex) : sum(negativeIndex, positiveIndex);
}
return sum;
}
private int sum(@NotNull Index mainIndex, @NotNull Index secondaryIndex) {
int localSum = 0;
// searching for the next suitable element
while (mainIndex.index < array.length && secondaryIndex.sign.test(array[mainIndex.index])) {
mainIndex.index++;
}
if (mainIndex.index < array.length) {
localSum += array[mainIndex.index++];
} else {
// add the remaining elements
for (; secondaryIndex.index < array.length; secondaryIndex.index++) {
if (secondaryIndex.sign.test(array[secondaryIndex.index])) {
localSum += array[secondaryIndex.index];
}
}
}
return localSum;
}
private static final class Index {
@NotNull
private final IntPredicate sign;
private int index;
public Index(@NotNull IntPredicate sign) {
this.sign = sign;
}
}
}
答案 2 :(得分:0)
选项1:
对数组进行就地排序并迭代一半以上。在每个步骤中,使用i
元素添加size-i-1
元素。
如果有一些大数字但很多小的负数(反之亦然),则不起作用。
选项2(改进):
就地排序。
保留两个索引 - 一个在开头,一个在结尾。他们见面时退出循环。在每一步,如果到目前为止的结果为负,则在第二个索引处添加值并使其前进。如果结果为正,则在第一个索引处添加值并将其前进。