How to find the smallest number that is common among the 3 arrays?

Viewed 963

This is the question:

Given 3 random arrays of integers, write a method to find the smallest number that is common among the 3 arrays. HINT: Sort first and just traverse first few elements until you reach the common number
  [-1,-2, 4,5,6,1,2,3,3,3,1,1,1]
  [54,6,7,8,1,3,5,1]
  [1,6,9,1,0,2,1]
  result = 1

I wrote a working solution code but I wonder if there is a simpler and more efficient way to do this.

import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;

public class Smcomm {

    public static void main(String[] args) {
        int[] A = {-1,-2,4,5,6,1,2,3,3,3,1,1,1};
        int[] B = {54,6,7,8,1,3,5,1};
        int[] C = {1,6,9,1,0,2,1};

        ArrayList<Integer> ar1 = new ArrayList<Integer>();
        ArrayList<Integer> ar2 = new ArrayList<Integer>();
        ArrayList<Integer> ar3 = new ArrayList<Integer>();

        for(int el: A){
            ar1.add(el);
        }

        for(int el: B){
            ar2.add(el);
        }

        for(int el: C){
            ar3.add(el);
        }

        Collections.sort(ar1);
        Collections.sort(ar2);
        Collections.sort(ar3);

        printer(ar1);
        printer(ar2);
        printer(ar3);

        finder(ar1,ar2,ar3);





    }


    static void printer(ArrayList ar){

        Iterator<Integer> it = ar.iterator();

        while(it.hasNext()){
            System.out.println(it.next());
        }
        System.out.println("--------------------");
    }

    static void finder(ArrayList ar1, ArrayList ar2, ArrayList ar3){
        ar1.retainAll(ar2);
        ar1.retainAll(ar3);
        if(ar1.size()>0){
            System.out.println(ar1.get(1));
        }else {
            System.out.println("no comm el");
        }
    }

}

The method that doesn't convice me totally is retainAll because I think have a complexity O(n^2).

I don't need to find all the elements but I want only to find the smallest. Do you know maybe if is possible someway to stop the execution of the method when it find the first element in common in the array? Shouldn't be difficult because the arrays are already sorted.

Thanks for the help.

6 Answers

A slightly different approach:

  • turn the shortest array into a sorted set
  • turn the two other arrays into ordinary sets
  • iterate that sorted set, and for each entry, check if the other two sets contain it.

The first number that both others contain must be the smallest common entry. That prevents you from looking at duplicate entries (which really don't matter here), and it also avoids sorting all three arrays.

In the end, for such small examples, any correct solution is good enough. The potential trade offs for different solutions only matter when you are be talking about lists with many thousands, even millions of entries.

First of all, there's no need to convert the arrays into Lists (since you don't need to use retainAll, which finds all the common elements of the 3 arrays, which is more than you need).

The sorting of the arrays (or Lists) seems necessary to ensure a worst case complexity of O(NlogN). I don't believe you can do better than that.

Once you have 3 sorted arrays, you can iterate over the 3 arrays with a single while loop using 3 indices (one for each array), until you find the first element that appears in all 3 arrays. This will take linear time.

One last thing - your current solution has a small bug - it should output ar1.get(0), not ar1.get(1).

You can use only Arrays and foreach loop to achieve this in a simple way:

  • Sorting the array
  • Loop through array A, and move B and C until number is > A.
  • Check if they are equals, otherwise go to next number, but avoiding going through same indexes again, continue from where it stopped.

Here it is an example of how to achieve this:

public static void main(String[] args) {
    int[] A = {-1, -2, 4, 5, 6, 1, 2, 3, 3, 3, 1, 1, 1};
    int[] B = {54, 6, 7, 8, 1, 3, 5, 1};
    int[] C = {1, 6, 9, 1, 0, 2, 1};

    Arrays.sort(A);
    Arrays.sort(B);
    Arrays.sort(C);

    Optional<Integer> inCommon = findInCommon(A, B, C);

    if (inCommon.isPresent()) {
        System.out.println(inCommon.get());
    } else {
        System.out.println("no comm el");
    }
}

private static Optional<Integer> findInCommon(int[] A, int[] B, int[] C) {
    int lastB = 0;
    int lastC = 0;

    for (int valA : A) {
        while (B[lastB] < valA) lastB++;
        while (C[lastC] < valA) lastC++;

        if (valA == B[lastB] && valA == C[lastC]) {
            return Optional.of(valA);
        }
    }
    return Optional.empty();
}

Another approach using Java 8 streams, where intersection is calculated and then find the minimum value:

 List<Integer> list1 = Arrays.asList(A);
 List<Integer> list2 = Arrays.asList(B);
 List<Integer> list3 = Arrays.asList(C);

 Set<Integer> commonElements = list1.stream().filter(list2::contains).filter(list3::contains).collect(Collectors.toSet());
 int minimumCommonElement = Collections.min(commonElements);

I think that the easiest way to achieve this would be to store your elements in a TreeSet (which is naturally sorted and removes duplicates) instead of an ArrayList, then just go through each element of set 1 until you find an element that exists in all 3 sets.

static void finder(Set<Integer> s1, Set<Integer> s2, Set<Integer> s3) {
    boolean found=false;
    for (int i: s1) {
        if (s2.contains(i) && s3.contains(i)) {
            System.out.println(i);
            found=true;
            break;
        }
    }
    if (!found) {
        System.out.println("no comm el");
    }
}

The following works pretty well. It occured to me that you only need to sort one of the lists and use that as the source from which to interate.

      List<Integer> list1 = new ArrayList<>(
      List.of(-1, -2, 4, 5, 6, 3, 1, 2, 3, 3, 3, 1, 1, 1));
      List<Integer> list2 = new ArrayList<>(List.of(54, 6, 7, 8, -4, 1, 3, 5, 1));
      List<Integer> list3 = new ArrayList<>(List.of(1, 6, 9, 1, 0, -4, 2, 1));

      Collections.sort(list1);
      for (int e : list1) {
         if (list2.contains(e) && list3.contains(e)) {
            System.out.println(e);
            break;
         }
      }

The idea here is that if e is in the smallest in list1 (the sorted list) then it if it is in list2 and list3 it must be the smallest common to all three.

One might gain some improvement by removing duplicates using sets. But there may be performance issues with that too unless the data was originally contained in sets to start.

Having just looked thru other answers, someone suggested sorting the smallest list (or set). That is an excellent idea I had not considered.

Related