Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is this function (for loop) space complexity O(1) or O(n)?

public void check_10() {
    for (string i : list) {
        Integer a = hashtable.get(i);
        if (a > 10) {
            hashtable.remove(i);
        }
    }
}

Would this be O(1) or O(n)? I'm guessing O(n), but isn't it reusing the spot of memory a each time making it O(1)?

like image 778
Intrepid Diamond Avatar asked Dec 28 '15 03:12

Intrepid Diamond


2 Answers

Space complexity asks "how much additional space (asymptotically, speaking) am I using in this snippet of code". Here's how a space complexity analysis would work, showing two general cases (for your code snippet):

Example 1: Pass-by-value hashtable and list

// assume `list` and `hashtable` are passed by value
public void check_10(List<String> list, HashMap<String, Integer> hashtable) {
    for (String i : list) {
        Integer a = hashtable.get(i);
        if (a > 10) {
            hashtable.remove(i);
        }
    }
}

Assuming you have N elements in hashtable and no elements are removed (i.e., a <= 10 for all N elements), at the termination of the loop, you will have N elements remaining in hashtable. Furthermore, each String in the N keys in the hashtable contains up to S characters. Lastly, each Integer in the N values in the hashtable is constant.

Similarly, you have a possible M number of strings in list, where each String may contain up to S characters.

Lastly, the Integer a does not contribute to the analysis because it references memory already accounted for. We can consider this Integer a constant memory still.

Therefore, assuming hashtable and list had been declared in the method, you are looking at a space complexity of O(N*S + M*S + I).

That said, asymptotically, we don't really care about I (the Integer a) because it is constant size that is likely much smaller than N and M. Similarly, S is likely much smaller than both N and M. This means the space complexity is O(N + M). Because both are linear terms, we can (carefully) reduce this to O(n), where n is a linear term that is a linear combination of N and M.

Example 2: Pass-by-reference hashtable and list or elsewhere declared (as in your example)

// assume `list` and `hashtable` are passed by reference or
// declared elsewhere in the class as in
//
// public void check_10() {
public void check_10(List<String> list, HashMap<String, Integer> hashtable) {
    for (String i : list) {
        Integer a = hashtable.get(i);
        if (a > 10) {
            hashtable.remove(i);
        }
    }
}

In this method, list and hashtable have already been allocated elsewhere, which means that the space complexity for this method is O(1) because we are only using constant space in Integer a and String i (even though technically, they are references to previously allocated memory -- you can consider the constant space as a result of storing the reference).

but isn't it reusing the spot of memory a each time making it O(1)?

It depends on what you mean by "reusing" the spot in memory. In theory, space complexity analysis does not exactly consider the implementation details of the language in this sense. This means that if you had a loop like

for (int i = 0; i < N; i++) {
    T myvar = new T();
}

you don't consider the implications of what's happening to myvar after each loop iteration. By "implications of what's happening" I mean, does the garbage collector reclaim memory after each iteration or are you continually allocating N spots of memory on the heap? In the GC case, it would be O(1) since you are reusing memory. In the "infinite" allocation case, it would be O(N) since you now have N spots allocated. Again, in theory, this is usually not considered in the analysis, and any T myvar = new T() is usually considered to be O(1) regardless of whether it sits in a loop or not.

In general, though, if you are referring to reusing the same spot in memory for list and hashtable each iteration, the answer is simpler. Consider the following:

public void foo() {
    int list[] = {1, 2, 3, 4};
    for (int i = 0; i < list.length; i++) {
        System.out.println(list[i]);
    }
}

Even though list is declared once and we're only iterating through list and printing the contents, foo() is still O(n) in memory complexity because we have allocated list, where in the asymptotic case could have up to n elements. Therefore, regardless of whether it reuses the same or different spots in memory, they both still contribute to a linear space complexity.

tl;dr

In your specific case, though, both list and hashtable had already been allocated elsewhere in the program and are not introduced here, so they do not contribute to the complexity, and Integer a and String i are only constant in memory. Therefore, this method will be O(1).

like image 83
Michael Recachinas Avatar answered Sep 26 '22 02:09

Michael Recachinas


It is O(1)

Other than 2 constant sized variables string i and Integer a this method does not allocate any extra space. Which means this loop clearly has constant space complexity .ie. O(1).

To clarify it further, I would rather ask you a question :

Do you call (iterative)binary search an O(n) space complexity algorithm ?

Absolutely not.

Your function check_10() uses a preallocated list and hash table (just like iterative binary search uses preallocated sorted array) and 2 constant space variables so it has O(1) space complexity.

PS : I am clarifying the doubt raised by OP in comments of this answer from here onwards ->

As pointed out by MichaelRecachinas, String and Integer in this loop are references. They are not a copy so they won't contribute anything to the space complexity of this function.

PPS: Integer a and String i are allocated memory only once and then are reused in each iteration of the loop.

like image 35
Rohit Rawat Avatar answered Sep 27 '22 02:09

Rohit Rawat