-2

So in my advanced algorithms class, we are to write an algorithm for a program to find two numbers in two sorted arrays of integers. The format is A[i] + B[j] == x. The runtime of the algorithm needs to be O(n). I thought i had it and wanted to check so I emailed my professor and she told me my runtime was O(n^2). Here is my code:

int[] A = {1,2,3,4,5};
int[] B = {1,2,3,4,5,6};
int x = 4;
int i = 0;
int j = 0;

for(int n = 0; n < (A.length*B.length); n++) {

    if(i >= A.length)
        i = 0;

    if(n % B.length == 0)
        j++;

    if(A[i] + B[j] == x) {
        System.out.println(A[i] + " + " + B[j] + " = " + x);
        break;
    }
    i++;
}

EDIT

I do apologize if this is still incorrect. I never really grasped the concept of Big-Oh. Would this change the runtime to O(n)? I got rid of the A.length*B.length and tried something a little different.

int[] A = {1,2,3,4,5};
int[] B = {1,2,3,4,5};
int x = 5;
int i = 0;
int j = 0;

while(i < A.length) {

    if(B[j] == x - A[i]) {
        /* exit */ }

    if(j >= B.length) {
        j = 0;
        i++; }

    j++;
}
Evan Henry
  • 15
  • 5
  • 1
    O(n^2) because (a.length * b.length) –  Jan 17 '18 at 22:40
  • Your professor is right. Did you have a question? – Joe C Jan 17 '18 at 22:41
  • Any suggestions on how to get the algorithm down to O(n)? – Evan Henry Jan 17 '18 at 22:42
  • @JoeC yes, I was just trying to get some feedback on how to fix it – Evan Henry Jan 17 '18 at 22:44
  • 1
    Technically, your solution is _O(mn)_, and your teacher want a solution that is _O(m+n)_. – Andreas Jan 17 '18 at 22:46
  • Technically, *O(mn)* might be the same as *O(n^2)*, depending on how you define *m* and *n*, and on what relationships, if any, you stipulate between them. Likewise *O(m+n)* and *O(n)*. – John Bollinger Jan 17 '18 at 22:53
  • @JohnBollinger Given input is two lists of disparate sizes, how do you define `n` such that solution is *_O(n^2)_*? – Andreas Jan 17 '18 at 23:01
  • That's a really, really confusing way to write a nested for-loop (note that you're pretty much just looping over both `i` and `j`, which could just be done with 2 for-loops over `i` and `j`, with no `n` - that'll be much more readable). I'm also fairly sure you'll miss some pairs (that should probably be `n%A.length`). – Bernhard Barker Jan 17 '18 at 23:10
  • @Dukeling I was under the impression that a nested for-loop would create O(n^2) so I tried to not use a nested for-loop. I must be wrong. – Evan Henry Jan 17 '18 at 23:16
  • Are the arrays always sorted? – Milhous Jan 17 '18 at 23:18
  • @EvanHenry If you're doing the same amount of work, it doesn't matter if there's 1 loop or 2000 loops, it's still the same running time. Just 2 loops over `i` and `j` would be (roughly) the same amount of work your code is currently doing. – Bernhard Barker Jan 17 '18 at 23:19
  • Similar: [Find a pair of elements from an array whose sum equals a given number](https://stackoverflow.com/q/4720271) – Bernhard Barker Jan 17 '18 at 23:19
  • @Milhous yes, the arrays will always be sorted – Evan Henry Jan 17 '18 at 23:21

3 Answers3

4

Solution 1:

Add all values in B to a Map with B value as the map key, and B-index as the map value.

Iterate A, and calculate desired B value as B = x - A. Look for it in the map, and if found, you then have the index.

You will only iterate A and B once each. Adding a single value to map is O(1), and looking up a value is O(1), assuming a HashMap, so overall is O(n).


Solution 2:

Iterate A ascending, and B descending.

For each value in A, look at current B value. Walk down B until A + B <= x (or you reach beginning of B).

You will only iterate A and B once each, so O(n).

Solution 2 requires less memory (no map), and is likely faster (no time spent building map).


UPDATE Here is code:

The above descriptions were based on need for index of values, and the code for each solution is:

Solution 1

private static void findSum(int[] a, int[] b, int x) {
    Map<Integer, Integer> bIdx = new HashMap<>();
    for (int j = 0; j < b.length; j++)
        bIdx.put(b[j], j);
    for (int i = 0; i < a.length; i++) {
        Integer j = bIdx.get(x - a[i]);
        if (j != null)
            System.out.println("a[" + i + "] + b[" + j + "] = " + a[i] + " + " + b[j] + " = " + x);
    }
}

Solution 2

private static void findSum(int[] a, int[] b, int x) {
    for (int i = 0, j = b.length - 1, sum; i < a.length && j >= 0; i++) {
        while (j >= 0 && (sum = a[i] + b[j]) >= x) {
            if (sum == x)
                System.out.println("a[" + i + "] + b[" + j + "] = " + a[i] + " + " + b[j] + " = " + x);
            j--;
        }
    }
}

Test

int[] a = {1,2,3,4,5};
int[] b = {1,2,3,4,5,6};
findSum(a, b, 4);

Output (same from both)

a[0] + b[2] = 1 + 3 = 4
a[1] + b[1] = 2 + 2 = 4
a[2] + b[0] = 3 + 1 = 4

Solution 1 using Set

If you don't need index position, then a Set is better for solution 1:

private static void findSum(int[] aArr, int[] bArr, int x) {
    Set<Integer> bSet = new HashSet<>();
    for (int b : bArr)
        bSet.add(b);
    for (int a : aArr)
        if (bSet.contains(x - a))
            System.out.println(a + " + " + (x - a) + " = " + x);
}

Output

1 + 3 = 4
2 + 2 = 4
3 + 1 = 4
Andreas
  • 147,606
  • 10
  • 133
  • 220
  • do u keep 2 variables to remember the index of `A` and `B` for solution 2? –  Jan 17 '18 at 23:06
  • You're iterating two different lists at the same time, one in ascending order, and one in descending order, so yes of course you need two index variables. – Andreas Jan 18 '18 at 00:04
  • for (int n = 0; n < A.length; n++){ b = x - A[n]; if(hashMap.get(b) != null){ System.out.println(A[n] + " + " + b + " = " + x);}} Sorry about the formatting the comment puts it in but I think this is how you were explaining the Solution 2. – Evan Henry Jan 18 '18 at 00:16
  • @EvanHenry No `hashMap` in solution 2. – Andreas Jan 18 '18 at 00:22
  • @Andreas Sorry, I meant solution 1 – Evan Henry Jan 18 '18 at 00:31
  • @Andreas So, I emailed my professor and she said that using a hashmap would not work. The reasoning behind it is because using the .get() method from a hashmap in the worst case has a time complexity of O(n). So the overall time complexity of the algorithm would be O(n^2) – Evan Henry Jan 18 '18 at 01:24
  • @EvanHenry The tell her that the hashing has a amortized performance of _O(1)_ for `put()` and `get()`, and that at the really high values of `n` where Big-O even makes sense, the amortized performance is true. Sure, worst case and at very low values of `n`, you might get _O(n)_, but that not what Big-O is trying to describe. The general rules of Big-O specifically ignores patterns at low values of `n`. --- Anyway, use solution 2. She can't say that about solution 2. – Andreas Jan 18 '18 at 03:54
0

Here is an example of how you can measure your time, i've included another method to find the numbers you mentioned. See the difference in runtime:

    int[] A = {1,2,3,4,5};
    int[] B = {1,2,3,4,5,6};
    int x = 4;
    int i = 0;
    int j = 0;

    long t1 = System.nanoTime();

    for(int n = 0; n < (A.length*B.length); n++) {

        if(i >= A.length)
            i = 0;

        if(n % B.length == 0)
            j++;

        if(A[i] + B[j] == x) {
            System.out.println(A[i] + " + " + B[j] + " = " + x);
            break;
        }
        i++;
    }
    long t2 = System.nanoTime();
    System.out.println("Time 1: "+(t2-t1));

    //Here's the other method
    long t3 = System.nanoTime();
    for (int n = 0;n<B.length;n++){
        for (int m =0;m<A.length;m++){
            if(A[m]+B[n]==x){
                System.out.println(A[m] +" + "+B[n] +" = "+ x);
            }
        }
    }
    long t4 = System.nanoTime();
    System.out.println("Time 2: "+(t4-t3));
Fernando Bravo Diaz
  • 343
  • 1
  • 5
  • 10
  • is the nested for-loop you wrote still O(n^2)? – Evan Henry Jan 17 '18 at 23:22
  • The time complexity for O(n^2) refers to quadratic time which tells that the amount of time for the program to run requires you to sort the items n^2 times. THe nested loop will search in each element of array A for each element of array B which still takes time to process so i don't think it will be O(n), it finds all the possible outcomes that obtain A[i] + B[i] = x, but if exposed to larger sized arrays it will deffinetly consume more time. – Fernando Bravo Diaz Jan 17 '18 at 23:41
0

Here is the code, for Andreas's Solution 1, that I came up with:

int[] A = {2,3,4};
int[] B = {7,9};
Map<Integer, Integer> hashMap = new HashMap<Integer, Integer>();
int x = 10;
int b;

for(int i = 0; i < B.length; i++) {
    hashMap.put(B[i], i);
}

for (int n = 0; n < A.length; n++){
    b = x - A[n];
    if(hashMap.get(b) != null)
        System.out.println(A[n] + " + " + b + " = " + x);
}
Evan Henry
  • 15
  • 5