Reputation: 117
Given an array of integers (positive and negative), each having at most K bits (plus the sign bit), and it is known that the sum of all the integers in the array also has at most K bits (plus the sign bit). Design an algorithm that computes the sum of integers in the array, with all intermediate sums also having at most K bits (plus the sign bit). [Hint: find in what order you should add positive and negative numbers].
This is a question from interview material not a homework
I am actually thinking of creating two separate arrays one for positive and other for negative, sort both of them and then add both so that most negative gets added to most positive... But this seems to have O(nlogn) time complexity(to sort) and O(n) space complexity> Please help!
Upvotes: 5
Views: 1264
Reputation: 4317
The main idea is to iterate the array with two indexes: for positive and negative elements. When the sum is negative we will search for next positive element (using corresponding iterator) to add to the sum, otherwise - for the negative one.
This code should work:
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;
}
}
}
Upvotes: 4
Reputation: 27307
First note that even if you let the immediate results overflow, the final result will always be correct if it can be represented. This is because integral types of any size act like cyclic groups under addition in most languages including Java (not in C, where integer overflow is undefined behavior, and C#, which is able to throw you an overflow exception).
If you still want to prevent overflow, here's how to perform it in-place and in linear time:
split the array in-place to its negative entries (in any order) and to its positive entries (in any order). Zero can end up anywhere. In other words, perform one quick-sort step with the pivot being zero.
Let ni
point to the start of the array (where negative entries are located).
pi
point to the end of the array.sum
be zero.While pi >= ni
sum
is negative
arr[pi]
to the sum
.arr[pi]
is negative (we've run out of positive addends) and sum
is positive (an overflow has occured), the result overflows.pi
arr[ni]
to the sum
.arr[ni]
is positive and sum
is negative, the result overflows.ni
.Finally, check if sum
has more than K
bits. If it does, declare the result overflows.
Upvotes: 7
Reputation: 7858
Option 1:
Sort the array in-place and iterate over half of it. At every step, add the i
th element with the size-i-1
th element.
Doesn't work if there's a few large numbers but many small negative numbers (or vice versa).
Option 2 (improvement):
Sort in-place.
Keep two indexes - one at the start and one at the end. Exit a loop when they meet. At every step if the result so far is negative add the value at the second index and advance it. If the result is positive add the value at the first index and advance it.
Upvotes: 0