I'd say the code suffers from readability more than from the efficiency aspects you (or your interviewer) had in mind.
The problem with such performance centric code is that usually the time spend to create and optimize the solution exceeds the time possibly saved during runtime of the program summed up over its lifetime by magnitudes.
And at the end it doesn't not tell something about your skills as a Java programmer. But thats the problem of your interviewer...
1) How can I improve the time and space complexity of my code?
removing unused variables would be a first step. You declare and increment k
but you never read it. But agreed, this is not a "killer"...
2) Is there a better way(lesser lines of code, better data structures) that can be used to improve the code?
The pure LoC metric has no meaning. The most important property of code (after correctness) is readability. Structuring your code in to small methods with distinct responsibilities and well chosen names is a much higher value then LoC, but its mot measurable...
so here is my suggestion. Especially look at the content of the for
loop how that makes a description of my intention what the code is supposed to do:
import java.util.ArrayList;
import java.util.List;
class Solution {
static int[] findDuplicates(int[] arr1, int[] arr2) {
List<Integer> ans = selectDuplicates(//
arr1.length < arr2.length ? arr1 : arr2, // shorter
arr1.length < arr2.length ? arr2 : arr1);// longer
return convertToIntArray(ans);
}
private static List<Integer> selectDuplicates(int[] shorter, int[] longer) {
ArrayList<Integer> duplicates = new ArrayList<>();
int indexInLonger = 0;
for (int current : shorter) {
indexInLonger = seekToEqualOrBiggerIn(longer, current, indexInLonger);
addMatchTo(duplicates, current, longer[indexInLonger]);
}
return duplicates;
}
private static int seekToEqualOrBiggerIn(int[] longer, int current, int indexInLonger) {
while (longer.length > indexInLonger && current > longer[indexInLonger])
indexInLonger++;
return indexInLonger;
}
private static void addMatchTo(ArrayList<Integer> duplicates, int current, int possibleMatch) {
if (current == possibleMatch)
duplicates.add(current);
}
private static int[] convertToIntArray(List<Integer> ans) {
return ans.stream().mapToInt(item -> item.intValue()).toArray();
}
}
selectDuplicates() forces the caller to check the length of the arrays? – Sharon Ben Asher
selectDuplicates()
is a private
method, an implementation detail not meant to be called by anyone else. So no caller is "forced" to check the length.
why? – Sharon Ben Asher
It is an optimization.
The idea is that I do things fastest when I don't do them.
With this optimization the length of the shorter array is implicitly checked by the foreach
loop. It saves me from incrementing and checking both indexed during the iteration.
It also reduces the iterations to the size of the smaller array since the bigger array must have some entries not in the smaller array.
this is an internal implementation that is meaningful only to the method and not the caller. – Sharon Ben Asher
Exactly! Thats why it is i my implementation not exposed to the outside.
and it doesn't even check if it got the correct arguments. – Sharon Ben Asher
what would a "correct" argument look like in your opinion?
it should be able to accept any two arrays
sort out which is shorter than which. you chould have a shorter() and longer() methods that do the actual checking and have selectDuplicates() call them.... – Sharon Ben Asher
The method findDuplicates()
which is the public interface does exactly that.
I also don't like the name of the methods. seekToEqualOrBiggerIn()
is way too implementation-specific.
But that is the main purpose of a private methods name: describe the current behavior of that method as detailed as possible.
what if the input is ordered in reverse? sure you will have to modify the code inside the method but the intention of the method remains the same: skip elements that are have "lower" order according to the order of the arrays.
– Sharon Ben Asherand
The OPs requirement explicitly mentioned ascending order. According to the YAGNY-principly I should not provide anticipated but not required flexibility. IMHO this also applies to naming too.
In fact if the requirement would change this name would actually make easier to find the part to change while skimming over the code. Of cause it would be renamed then.
also, addMatchTo()
does not hint that the match is not yet certain and that a condition is applied. – Sharon Ben Asher
At least to me it does. And no question: The receiver of a message determines its content.
and I don't understand the reason for this particular breaking up of the code into methods. why do you have findDuplicates()
and selectDuplicates()
? this seems arbitrary separation.
findDuplicates()
is part of the public API (fixed by the interviewer I guess).
The OPs implementation consist of two logical parts:
- find and collect the duplicates in a list
- convert that list into an array as expected by the methods signature.
To me this are two different responsibilities which should live in their own methods according the the single responsibility principle.
Since Java does not allow methods signatures to differ only in the return value I needed a different but similar name.