What is the best and efficient way to get the maximum i, which is the number of rows and j, which is the number of columns, in a two dimensional array?
Hopefully, the time complexity can be lower than O(n) for every case. No loop here and can still find the maximum j.
For example, if I have an array like this one
[
[18,18,19,19,20,22,22,24,25,26],
[1,2,3],
[0,0,0,0]
]
Then I want to get i = 3 and j = 10 here as a result.
Can anyone help me?
You can avoid writing the loop yourself, but you can't avoid having a runtime of at least O(n), since "someone" needs to loop the source array.
Here is a possible way to do that in Java 8:
Arrays.stream(arr).map(row -> row.length).max(Integer::compare).get();
This returns the maximum length of a "row" in your 2d array:
10
Another version which avoids using the Comparator and therefore might be a bit easier to read:
Arrays.stream(arr).mapToInt(row -> row.length).max().getAsInt();
arr is supposed to be your source array.
Edit: the older version used .max(Integer::max), which is wrong and causes wrong results. See this answer for an explanation.
Assuming your array does not contain null values, you could write something like this:
private static final Comparator<int[]> lengthComparator = new Comparator<int[]> () {
#Override
public int compare(int[] o1, int[] o2) {
return o1.length - o2.length;
}
};
#Test
public void soArrayMaxLength() {
int[][] array = new int[][] {
{18,18,19,19,20, 22, 22, 24, 25,26},
{1,2,3},
{0,0,0,0}
};
int i = array.length;
Optional<int[]> longestArray =
Arrays.stream(array)
.max(lengthComparator);
int j = longestArray.isPresent() ? longestArray.get().length : 0;
System.out.println(String.format("i=%d j=%d", i, j));
}
If you happen to create a parallel stream from the array instead, you could speed up this even further.
Another option is to sort the array by length, the quicksort usually has an average complexity of O(n*log(n)) therefore this isn't faster;
int i = array.length;
Arrays.parallelSort(array, lengthComparator);
int j = array[i-1].length;
System.out.println(String.format("i=%d j=%d", i, j));
Your i is the number of rows, which is simply the length of the 2-D array (assuming you are OK with including empty/null rows in this count).
The max row length j, however, would require iterating over all the rows to find the row i having the maximum arr[i].length.
There will always be a loop1, even though the looping will be implicit in solutions that use Java 8 streams.
The complexity of getting the max number of columns is O(N) where N is the number of rows.
Implicit looping using streams probably will be less efficient than explicit looping using for.
Here's a neat solution using a for loop
int max = o;
for (int i = 0; i < array.length; i++) {
max = Math.max(max, array[i].length);
}
This works in the edge-case where array.length == 0, but if array or any array[i] is null you will get a NullPointerException. (You could modify the code to allow for that, but if the nulls are not expected, an NPE is probably a better outcome.)
1 - In theory, you could unroll the loops for all cases of array.length from 0 to Integer.MAX_VALUE, you would not need a loop. However, the code would not compile on any known Java compiler because it would exceed JVM limits on bytecode segments, etcetera. And the performance would be terrible for various reasons.
You could try this way: loop on the array and find the max length of the arrays which is in this array
byte[][] arrs = new byte[3][];
int maxLength = 0;
for (byte[] array : arrs) {
if (maxLength < array.length) {
maxLength = array.length;
}
}
Related
Implement a method that checks whether an integer is present in both integer array parameter 1 and integer array parameter 2 and prints the result of the search, with the best performance you can. The method parameters are: (1) the first integer array and (2) the second integer array of the same size as parameter 1 and (3) the integer to search for.
Note - Consider better performance to mean that a better performing method requires fewer general work steps to solve the problem with the same size of arrays. You may want to review the Java SE API page for java.util.Arrays
I was able to implement the solution but I am not sure if it the best-performing one because I am not using any java.util.Arrays methods as I am not sure which one to use necessarily to get me the best answer
public static void findCommonElements(int[] arr1, int[] arr2, int num){
for(int i = 0; i < arr1.length; i++){
for(int j = 0; j < arr2.length; j++){
if(arr1[i] == arr2[j] && arr1[i] == num){
System.out.println(num);
}
}
}
}
UPDATE:
I was able to update the code with following solution which completely removes for loop and implements binary for better performance
int[] arr1 = {7,8,5,1,2,3,6,7};
int[] arr2 = {9,8,6,4,1,2,4,5};
Arrays.sort(arr1);
Arrays.sort(arr2);
int index1 = Arrays.binarySearch(arr1, 5);
int index2 = Arrays.binarySearch(arr2, 5);
System.out.println(index1);
System.out.println(index2);
if(index1 < 0 || index2 < 0){
System.out.println("number not found in both arrays");
}
else{
System.out.println("number found in both arrays");
}
The problem description is a bit hard to follow, but by reference to the example code, I take this to be a fair rewording: "Write the best-performing method you can that takes two int arrays of the same length and a scalar int value i as parameters, and prints whether the value of i appears in both arrays."
Your first solution tests each pair of elements drawn one from the first array and the other from the second to determine whether they are equal to each other and to the target value. This is grossly inefficient for the problem as interpreted.
Your second solution sorts the arrays first, so as to be able to use a binary search to try to find the target element. This is better, but still inefficient. Although the binary searches are quite fast, the sorting required to prepare for them takes a lot more work than is saved by a single binary search.
Since it is sufficient to determine only whether the target value appears in both arrays, you can
scan each array for the target value, once, independently of the other.
skip the second scan if the first one does not find the target value
break early from each scan when the target value is found
The latter two are minor improvements, as they reduce only the minimum and average number of steps. The first, however, is a huge improvement, especially as array size increases, because for arrays of length n, then this requires a number of steps proportional to n in the worst case, whereas your first example code requires steps proportional to n2 in both the average and worst cases, and your second requires time proportional to n log n in the average and worst cases.
The implementation is left as the exercise it is intended to be. However, with respect to
I was able to implement the solution but I am not sure if it the
best-performing one because I am not using any java.util.Arrays
methods as I am not sure which one to use necessarily to get me the
best answer
, I don't think java.util.Arrays offers any method that particularly helps with this problem, especially given the orientation toward best possible performance.
You can use search the arrays using streams:
public static boolean findCommonElements(int[] arr1, int[] arr2, int num) {
return Arrays.stream(arr1).anyMatch(x -> x == num) &&
Arrays.stream(arr2).anyMatch(x -> x == num);
}
Similar method using linear search in arrays of Integer using Arrays.asList to convert arrays:
public static boolean findCommonElements(Integer[] arr1, Integer[] arr2, int num) {
return Arrays.asList(arr1).indexOf(num) > -1 &&
Arrays.asList(arr2).indexOf(num) > -1;
}
I want to iterate just the half of an array in java. Is there any elegant way to shorten this up, eg with a for-each loop?
int[] array = {0,1,2,3,4,5};
for (int i = 0; i<array.length/2; i++)
{
System.out.println(array[i]);
}
If you converted the array into a list using the asList method of the Arrays class in Java, then you can use the forEach method in the List class in Java to print out each element of the list in one single line,
Arrays.asList(array).forEach(System.out::println);
To print only half the array, I'd suggest copying half the array into a new array using the copyOfRange method,
Integer[] newArray = Arrays.copyOfRange(array, 0, array.length/2);
Arrays.asList(newArray).forEach(System.out::println);
EDIT: Like Marko Topolnik pointed out, we're actually starting out with an array of primitive types instead of object types, so in order to use the asList method we're going to have to convert the array into an array of objects (from int to Integer using Integer[] integerArray = ArrayUtils.toObject(array);). However this just seems tedious/inefficient and OP asked for a shorter way so my suggestion would be to use Marko's method,
Arrays.stream(array).limit(array.length/2).forEach(System.out::println);
EDIT 2: Like Amber Beriwal pointed out, it should be noted that although the one-line solution above looks pretty due to its conciseness, it is still very inefficient/slow compared to the OP's original method. Therefore, I would like to reiterate Amber's comments that the OP and others should just stick with the original for-loop.
for (int i = 0; i < array.length/2; i++)
{
System.out.println(array[i]);
}
How about:
IntStream.range(0, array.length / 2).map(i -> array[i]).forEach(System.out::println);
One line, and no array copies.
Broken down:
IntStream.range(0, array.length / 2) //get the range of numbers 0 - (array length)/2
.map(i -> array[i]) //map from index to value
.forEach(System.out::println); //print result
The answer you have posted is good. Although, I couldn't find a better way to make it compact keeping the performance same, but performance can be improved. Remember following practices while coding:
Algorithm's memory requirement should be optimum
Algorithm's time i.e. performance should be optimum
Algorithm's complexity should not be too much. For significant gains in 1 & 2, this can be skipped.
Considering 1 & 2, lines of code comes at least priority.
Solution 1: This solution will be 4-5 times slower than your approach, plus Stream will take extra space.
Arrays.stream(array).limit(array.length/2).forEach(System.out::println);
Solution 2: This solution is faster than the above code and your code (based on my testing), but Stream will take extra space. Also, it is not compact.
Arrays.stream(array).limit(array.length / 2).forEach(new IntConsumer() {
#Override
public void accept(int value) {
System.out.println(value);
}
});
Solution 3: As suggested by you.
int[] array = new int[] { 0, 1, 2, 3, 4, 5 };
int limit = array.length / 2;
for (int i = 0; i < limit; i++) {
System.out.println(array[i]);
}
Recommendation: Don't go over to reduce the LOC at the stake of losing performance and memory. It is better to keep up with the solution that gives you best performance..
Basically what I want is to I skip elements on those index values which are there in the set otherwise I should just push the old array elements into the new array.
So if my set contains [2, 4, 9, 10] I should skip the values at index 2,4,9,10 in the old Array and put the values at othe other index locations in my new Array.
I am writing code like this
int[] newArr = new int[oldArray.length - set.size()];
for(int i = 0, j = 0; j < newArr.length && i < oldArray.length; j++,i++){
if(set.contains(i) )
i++;
else
newArray[j] = oldArray[i];
}
I am creating and filling my set like this
Set<Integer> commonSet = new HashSet<>();
for(int i = 0; i < array1; i++ ){
for(int j= 0; j < array2; j++) {
if(arrayOne[i] == arrayTwo[j]){
commonSet.add(i);// Here I am saving the indices.
}
}
}
Not Sure if this is the best way. Is there any other way which would be more efficient?
Or I must have to resort to Collection classes like ArrayLists.
Using Collection classes instead of arrays would make your code much simpler.
Doing array subtraction using common libraries like apache CollectionUtils looks like this:
Collection<Integer> diff = CollectionUtils.subtract(Arrays.asList(array1), Arrays.asList(array2));
Unless you're going to be working very large sets of data, it won't have a noticeable impact on speed.
Also, creating a set of different indexes the way you do above is going to scale very poorly for larger data sets. Just calculating the times for doing a difference using CollectionUtils.subtract() vs your set creation code shows the scaling problems (arrays filled with random Integers):
array1.length = 1000
array2.length = 1000
diff.size() = 530
elapsed ms for calculating diff = 39
set creation ms = 7
array1.length = 10000
array2.length = 10000
diff.size() = 5182
elapsed ms for calculating diff = 47
set creation ms = 519
array1.length = 50000
array2.length = 50000
diff.size() = 26140
elapsed ms for calculating diff = 101
set creation ms = 12857
array1.length = 1000000
array2.length = 1000000
diff.size() = 524142
elapsed ms for calculating diff = 1167
(didn't bother to wait for the program to finish)
As you can see, doing a double loop to compare every element scales quite poorly, and that's not even counting the subtraction you'll have to do afterwards.
EDIT updated to reflect changes in the question
If you're worried about performance, definitely do not use any list or collection classes. They are notorious for re-allocating arrays frequently as they need more capacity, which is a very slow operation.
Unfortunately, I don't know how you create/fill the set of indices. If it is possible for you to have your set in an array as well and generate it in such a way that its entries are sorted, you can optimize your code significantly.
If set is fairly long compared to oldArray, do this (this assumes no duplicate entries in set!):
int l = oldArray.length; // Cache length (some compilers might do this for you)
for (int i=0, j=0, k=0; i<l; i++) {
if (set[k]==i) {
k++;
} else {
newArr[j++] = oldArray[i];
}
}
If set is fairly short, do this (this can handle duplicate entries, but set still needs to be sorted):
int o1=0;
int o2=0;
for (int p:set) {
System.arraycopy(oldArray, o1, newArr, o2, p-o1);
o1+=p+1;
o2+=p;
}
System.arraycopy(oldArray, o1, newArray, o2, oldArray.length-o1);
The former avoids function calls and the latter banks on the optimized memory-copy implementation of System.arraycopy(...) (and set can be any sorted Iterable, although an array will be faster).
Which one is faster will depend on the exact sizes of your arrays and which system (CPU, JVM) you use.
If set is not sorted, you can either use your approach (debugged, of course) or you can sort it first and then use one of the approaches here. Again, which one will give you better performance will depend on the size of set and your system.
This piece of code is doing it for me.
Thanks # Patricia Shanahan
int j = 0, i = 0;
while( j < newArr.length && i < oldArray.length){
if(commonSet.contains(i)){
i++;
}
else{
diffArray[j] = arrayOne[i];
j++;
i++;
}
}
Heap - Sort Algorithm
The problem I am having is this, this algorithms n input is 2, this is designed so that the 1st position (int i) of the array and the 2nd position (int j) have their values compared.
The problem is that this ignores the 0 position of the given array list. I have tried reducing certain values, this will create infinite loops. The algorithm is an adaptation of pseudocode. It isn't designed to run arraylist from 0. I can't think of how to re-adapt this algorithm into a decent minimum heap sort.
public static void input( ArrayList<input> vertexList, int n )
{
int j=n;
int i=n/2;
input object = vertexList.get(n);
while ((i>0) && vertexList.get(i)> object){
vertexList.set(j, vertexList.get(i));
j = i;
i = i/2;
}
vertexList.set(j, object);
}
try to use vertexList.get(i-1) and vertexList.get(j-1) and vertexList.set(j-1, ...)
Recently, I tried to solve Problem 23 of Project Euler. For that I first create a list of all abundant numbers, called abundants.
Next I iterate over this list and build another list of all sums of abundant numbers that are below a certain limit. Now I noticed something strange. I use a nested loop to iterate twice over the list. But if I use an array to store the sum it takes some seconds, if I add the sums to an ArrayList it takes hours. What's the reason for that? I thought the costly operation are the two nested loops, but it seems the costly operation is ArrayList#add. Any hints why this is the case?
Here the code for the array:
for (int i = 0; i < abundants.size(); i++) {
for (int j = 0; j < abundants.size(); j++) {
int tot = abundants.get(i) + abundants.get(j);
if (tot <= limit)
isSum[tot] = true;
}
}
}
Here the code for the ArrayList:
ArrayList<Integer> sums = new ArrayList<Integer>();
for (int i = 0; i < abundants.size(); i++) {
for (int j = 0; j < abundants.size(); j++) {
int s = abundants.get(i) + abundants.get(j);
if (!sums.contains(s) && s < limit) {
sums.add(s);
}
}
}
Your ArrayList implementation is O(n^3) whereas the other is O(n^2): sums.contains(...) has to traverse the entire sums list for every iteration of your inner loop.
I think rather that your problem is in ArrayList#contains, which has to traverse the whole list, thus raising your complexity to O(n^3), as opposed to O(n^2) of the program #1.
Your code isn't equivalent, the .contains() is more expensive than what you are doing with the raw array. The .contains() walks the entire array every time is called, you don't do this in the raw array based version.
Because int can be much faster than Integer.
Try using Integer[] in the first case or TIntArrayList in the second case for comparison.
If you know the (maximum) number of the elements, try to initialize the Array list with a given size:
ArrayList<Integer> sums = new ArrayList<Integer>(abundants.size() * abundants.size());
With that the ArrayList won't have to be resized, this will increase the speed.