Intro: As far as I could search, this question wasn\'t asked in SO yet.
This is an interview question.
I am not even specifically looking for a code sol
Use dynamic programming.
array[0] + array[2]
), so this is a trivial step.min(array[0], array[1])
).The whole algorithm is O(n), since both widening and updating are constant-time operations. The algorithm can be proved correct by simple induction. O(n) is also a lower bound since we have to consider every element of the array, so this algorithm is optimal.
Find the four smallest and consider all possibilities among those four. The smallest is nonadjacent to at least one of the second, third, or fourth smallest; the only other possibility that could be better is the second and third smallest (assuming that they are nonadjacent).
How about that: you find k
smallest numbers (or more precisely their indices) (k
big enough, let say 10
). It is sure, that the wanted pair is between them. Now you just check the possible 50
pairs and select the best which satisfies the constraints.
You don't need 10
, less would do - but more than 3
:)
Edit: finding k
smallest numbers is O(n)
, because you just keep the best 10
for example in a heap (add new element, delete maximum O(k*logk)=O(1)
operations).
Then there will be a pair which satisfy the constraints (not next to each other). It is also clear that, if you build the sum with an element not from those k
, it would be bigger than the best pair chosen from those k
elements.
Checking at most k*k
pairs is also O(1)
, thus the whole running time is O(n)
.
edit: you're right, I completely ignored the adjacency constraint. luckily I've thought of a solution. The algorithm goes like this:
(O(n))
(O(n))
O(1)
- just an index check) O(n)
) I think this does not need any deep reasoning, and can be solved in a single pass, keeping the optimal solution of the array elements processed so far:
public static int[] minimumSumOfNonAcjacentElements(int[] a) {
// the result for the sequence a[1:i]
int minSum = Integer.MAX_VALUE;
int minSumElement1 = Integer.MAX_VALUE;
int minSumElement2 = Integer.MAX_VALUE;
// the minimum element eligible for joining with a[i], i.e. from a[1 : i-2]
int minElement = a[1];
int prevElement = a[2]; // a[i - 1]
for (int i = 3; i + 1 < a.length; i++) {
int sum = minElement + a[i];
if (sum < minSum) {
minSum = sum;
minSumElement1 = minElement;
minSumElement2 = a[i];
}
if (prevElement < minElement) {
minElement = prevElement;
}
prevElement = a[i];
}
return new int[] {minSumElement1, minSumElement2};
}
Here's some test code, with the corner cases from OP's question:
private static void test(int minSumIndex1, int minSumIndex2, int... input) {
int[] result = minimumSumOfNonAcjacentElements(input);
if (result[0] == minSumIndex1 && result[1] == minSumIndex2) {
// ok
} else {
throw new AssertionError("Expected: " + minSumIndex1 + ", " + minSumIndex2 + ". Actual=" + Arrays.toString(result));
}
}
public static void main(String[] args) throws Exception {
test(2, 2, 4, 2, 1, 2, 4);
test(1, 2, 2, 2, 1, 2, 4, 2, 6);
test(1, 2, 0, 2, 1, 2, 4, 2, 0);
System.out.println("All tests passed.");
}
Find the second smallest that is not a neighbour of the first one and not the first or last one in the array. Then build the sum.
Otherwise calculate the sum of both neighbours of the first number. check if its smaller then the first sum
This will always work because if the first sum is not the answer that means the first number cannot be part of the solution. And that on the other hand means, the solution can just be the second sum.