9
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)?

Intrepid Diamond
  • 462
  • 1
  • 6
  • 16

3 Answers3

8

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).

Community
  • 1
  • 1
Michael Recachinas
  • 2,739
  • 2
  • 20
  • 29
  • Thanks! Is it necessary to denote that n = N+M or is it okay to just declare O(n) space complexity (when reduced)? Is it pretty much the same concept of O(2n) -> O(n)? – Intrepid Diamond Dec 28 '15 at 04:35
  • It's safe to say the complexity is linear, so as long as you recognize the contributors to the linear complexity, O(n) should suffice. Yes, it is roughly the same concept as O(2n) -> O(n). – Michael Recachinas Dec 28 '15 at 04:36
  • Thanks! One last thing... Vector remove(item): I understand that time complexity is O(n) since there is shifting to be done (unless at the end). What would the space complexity be with shifting? – Intrepid Diamond Dec 28 '15 at 04:50
  • I'm currently answering your other question which should shed light on this. – Michael Recachinas Dec 28 '15 at 04:55
  • 1
    @MichaelRecachinas no it has O(1) space complexity. Hash table and list are already allocated, they don't count. – Rohit Rawat Dec 28 '15 at 06:15
  • 1
    @RohitRawat Good catch. As I was writing the answer, I had thought that they'd been declared in the method. Edited. – Michael Recachinas Dec 28 '15 at 06:24
1

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.

Rohit Rawat
  • 144
  • 2
  • 11
  • Thanks for the correction. Could you please clarify why string i and Integer a are constant sized? Before I thought the amount of space was based off the amount of elements in the list. Does memory not allocate a new string i and Integer a for each element in the list (does it reuse the same space in memory)? – Intrepid Diamond Dec 28 '15 at 10:15
  • @FrostCantelope : Will the length of `string i` be dependent on the number of elements in `list` or `hashtable` ? (I can't find any other input to this function). If not so, then it should have any constant upper bound. This implies it will take constant space ( be it 1 million, but that would still be a constant space). – Rohit Rawat Dec 28 '15 at 10:21
  • No to length question. I'm kinda confused about how it's constant. Let's say there are 5 elements in the list. This would mean 5 string i's and 5 Integer a's for a total of 10 variables. Since the amount of variables is based off the amount of elements, wouldn't this mean that memory is based off n? What am I not understanding? Thanks! – Intrepid Diamond Dec 28 '15 at 10:33
  • Or am I wrong and the string i and Integer a spots in memory are being reused each time and the program only uses 2 spots of memory the whole time? – Intrepid Diamond Dec 28 '15 at 10:35
  • 1
    @FrostCantelope yes, `i` and `a` are reused in the loop whole time. Each time when 1 iteration is completed then `i` and `a` are initialized to their new value in next iteration. – Rohit Rawat Dec 28 '15 at 10:39
  • That makes complete sense. Thank you! – Intrepid Diamond Dec 28 '15 at 10:40
  • 1
    It should be further clarified that in this for-each loop, `String i` is not actually allocated memory. It is merely an iterator that's referencing an element in `list`, and therefore does not contribute to the analysis (as it is referencing previously allocated memory). Similarly, `hashtable.get` returns a reference, so `Integer a` is not allocated memory, but rather a reference to previously allocated memory (outside of the function). This doesn't change the fact that the function is O(1). – Michael Recachinas Dec 28 '15 at 15:18
  • 1
    The clarification is, however, fairly pedantic, and in general you would still consider `String i` and `Integer a` as having constant memory and thus O(1). – Michael Recachinas Dec 28 '15 at 15:26
  • @MichaelRecachinas : `String i` is **not an iterator** and `Integer a` is a object, **not a reference**. You can try changing their values in loop and original values in list and hashtable wont be affected. – Rohit Rawat Dec 29 '15 at 05:14
  • 1
    @xordux `HashMap.get` returns a reference to the object in the HashMap because in Java, Objects are passed by reference. (To be completely pedantic: Java passes Object references by value, so you _are_ getting a copy of the memory address to the Object from `HashMap.get`.) (See http://stackoverflow.com/questions/764837/the-get-function-for-java-hashmaps and http://www.javaworld.com/article/2077424/learn-java/does-java-pass-by-reference-or-pass-by-value.html). You can change their values in the loop as you said, but this does not mean they aren't _references to objects_. – Michael Recachinas Dec 29 '15 at 14:26
  • 1
    @xordux They aren't C-style references that disallow reassignment. "Reference" in Java is more akin to pointer in C. With regard to `String i`, _behind the scenes_ Java `foreach` *is* actually equivalent to using an iterator (see http://stackoverflow.com/questions/28351773/does-java-foreach-create-copies). It does not mean we see an iterator. Rather, we see a "reference" to memory in `list`. Note I'm only trying to suggest that it's not making a copy of each element in `list` and placing it in `i` (or similarly `a`). – Michael Recachinas Dec 29 '15 at 14:31
  • 1
    I do realize that space complexity analysis doesn't really care about implementation details of the language (as I stated in my answer). Again, I previously mentioned I am being overly pedantic here. – Michael Recachinas Dec 29 '15 at 14:41
  • 1
    Here's an example illustrating how `Integer a` is a reference to an object: http://ideone.com/b2J5BM. `String i` is a reference to an object in the list. To clarify what I previously mentioned, behind the scenes, this `foreach` loop is just syntactic sugar for using an `Iterator` and calling `Iterator.next()`, thus giving you the `String` (which in turn is a reference to an object in the list). – Michael Recachinas Dec 29 '15 at 19:49
  • @MichaelRecachinas : Amazingly it didn't worked same for Integer arrayList yesturday for me. Here is the code http://ideone.com/ykZmZd Any idea why values remained intact in my code ? Maybe I should post it as a new question because this is becoming very off-topic. – Rohit Rawat Dec 30 '15 at 05:13
  • 1
    @MichaelRecachinas After a research I have realized that java references works are differently than C++ references. Thank you for your patience, it really helped. – Rohit Rawat Dec 30 '15 at 05:28
  • 1
    @xordux No problem. Just wanted to make sure we're on the same page. :) – Michael Recachinas Dec 30 '15 at 05:34
0

This has O(n) space complexity because the list takes up that space. :). The hash table is at most another O(n), so the sum of the space complexities is still O(n).

Untitled123
  • 1,317
  • 7
  • 20
  • 1
    Do you call (iterative)binary search an O(n) space complexity algorithm just because it uses an array ? – Rohit Rawat Dec 28 '15 at 06:15
  • @RohitRawat Be careful with this statement -- it does depend on whether in `int binary_search(T arr[], T val)` the array was passed by value or passed by reference. If we assume it's passed by value, and thus a copy of the array is made, then it indeed would be O(n) in space. If it's passed by reference, then as you imply, it is O(1). (Examples provided here: https://www.cs.northwestern.edu/academics/courses/311/html/space-complexity.html) – Michael Recachinas Dec 28 '15 at 15:29