Home > database >  Java - Time Complexity O(N**2)
Java - Time Complexity O(N**2)

Time:12-18

I'm practicing on Codility. There is an Easy Level question, yet I'm stuck on performance. The test result analysis marks this code as O(N**2), but obviously there are not any nested loops. Can anyone tell me why my code is O(N**2)?

public static int solution(int X, int[] A) {
    List<Integer> temp = new ArrayList<>();
    for (int i = 1; i <= X; i   ){
        temp.add(i);
    }

    int counter = 0;
    int res = -1;
    for (int i: A){
        if (temp.contains(i)) {
            temp.remove(new Integer(i));
        }
        if (temp.size() == 0){
            res= counter;
            break;
        }
        counter  ;
    }

    if (temp.size() != 0){

        res = -1;
    }
    return res;
}

CodePudding user response:

The test result analysis marks this code as O(N**2), but obviously there are not any nested loops. Can anyone tell me why my code is O(N**2)?

Asymptotic complexity is not (just) about loop counting. You have a loop over the elements of A, and within it you invoke temp.contains(), and conditionally temp.remove(). For an ArrayList, the cost of each of these is proportional to the number of elements in temp.

Overall, then, if N is A.length then the asymptotic complexity of your method is O(X * N). Codility's analysis seems not quite right, but if X cannot be taken as bounded by a constant then your code nevertheless is more complex than O(N). If Codility performed an heuristic analysis that introduced an artificial relationship between X and N, then subject to that functional relationship, your method could indeed be O(N2).


You can definitely do better. Your method appears to be computing the length of the smallest initial sub-array of A that contains all of the integers between 1 and X. To do this efficiently, you do need some kind of mechanism to track what values have been seen, but containment in a List is an expensive choice for that. Consider instead an array of boolean to track which specific values have been seen, and a separate count of how many have been seen:

boolean[] seen = new boolean[X   1];
int numSeen = 0;

With that in hand, loop over the elements of A, and for each element i that is in the range 1 ... X, test seen[i] (costs O(1) per test). If true, do nothing. If false, set seen[i] to true (O(1)), increment numSeen (O(1)), and test whether numSeen has reached X (O(1)). Return the number of elements that have to be examined before numSeen reaches X, or -1 if numSeen never does reach X. (Details left as an exercise.)

With that, every loop iteration performs O(1) work regardless of any bound on X, and that O(1) work is in fact cheap, which is a different consideration. Overall: O(N), and pretty efficient, too.

CodePudding user response:

That is because of the use of contains. temp is an ArrayList, and contains performs a linear lookup. Within a for loop, that will become O(N²).

CodePudding user response:

The other loop is hidden in the remove method of ArrayList. The remove method of ArrayList is O(N) because it has to shift the elements to fill the gap.

for (int i: A){   // ===> O(N)
        if (temp.contains(i)) {
            temp.remove(new Integer(i));  //===> O(N)
        }
        if (temp.size() == 0){
            res= counter;
            break;
        }
        counter  ;
    }
  • Related