这个函数(for loop)是空间复杂度O(1)还是O(n)?

时间:2015-12-28 03:23:33

标签: java algorithm data-structures big-o space-complexity

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

这是O(1)还是O(n)?我猜O(n),但是每次重复使用内存点时都不能使用O(1)?

3 个答案:

答案 0 :(得分:7)

空间复杂性问“我在这段代码中使用了多少额外空间(渐近,说话)”。以下是空间复杂度分析的工作原理,显示了两个一般情况(对于您的代码片段):

示例1:按值传递hashtablelist

// 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);
        }
    }
}

假设N中有hashtable个元素且没有删除任何元素(即所有a <= 10元素都为N),在循环结束时,您将N中剩余hashtable个元素。此外,StringN个键中的每个hashtable都包含最多S个字符。最后,IntegerN值中的每个hashtable都是常量。

同样,M中的字符串数量可能为list,其中每个String最多可包含S个字符。

最后,Integer a对分析没有贡献,因为它引用了已经占用的内存。我们仍然可以考虑这个Integer a常量记忆。

因此,假设在方法中声明了hashtablelist,那么您正在考虑O(N*S + M*S + I)的空间复杂度。

也就是说,渐渐地,我们并不关心IInteger a),因为它的大小可能比NM小得多。同样,S可能比NM小得多。这意味着空间复杂度为O(N + M)。由于两者都是线性项,我们可以(小心地)将其减少到O(n),其中n是一个线性项,是N and M的线性组合。

示例2:在其他地方声明的hashtablelist 传递(如您的示例中所示)

// 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);
        }
    }
}

在此方法中,listhashtable已在其他位置分配,这意味着此方法的空间复杂度为O(1),因为我们仅在{{1}中使用常量空间和Integer a(尽管从技术上讲,它们是对先前分配的内存的引用 - 您可以考虑存储引用的常量空间。)

  

但不是每次重复使用记忆点的时候都是O(1)吗?

这取决于你在内存中“重用”这个位置的含义。从理论上讲,空间复杂性分析并没有从这个意义上准确地考虑语言的实现细节。这意味着,如果你有像

这样的循环
String i

在每次循环迭代后,你没有考虑for (int i = 0; i < N; i++) { T myvar = new T(); } 发生的事情的影响。通过“对正在发生的事情的影响”我的意思是,垃圾收集器是在每次迭代后回收内存还是你不断在堆上分配N个内存点?在GC情况下,它是myvar,因为 重用内存。在“无限”分配情况下,它将是O(1),因为您现在已经分配了O(N)个点。同样,理论上,这通常不在分析中考虑,并且任何N通常被认为是O(1),无论它是否位于循环中。

一般而言,如果您指的是在每次迭代中重用T myvar = new T()list内存中的相同位置,答案就更简单了。请考虑以下事项:

hashtable

即使public void foo() { int list[] = {1, 2, 3, 4}; for (int i = 0; i < list.length; i++) { System.out.println(list[i]); } } 被声明一次而我们只是通过list进行迭代并打印内容,list仍然是内存复杂度的O(n),因为我们已经分配了{{} 1}},在渐近情况下,最多可以有foo()个元素。因此,无论它是否在内存中重用相同或不同的点,它们都会导致线性空间复杂性。

TL;博士

在您的特定情况下,listn已经在程序的其他位置分配,并且未在此处介绍,因此它们不会导致复杂性,{{1} }和list在内存中只是常量。因此,此方法将为hashtable

答案 1 :(得分:1)

是O(1)

除了2个常量变量string iInteger a之外,此方法不会分配任何额外空间。这意味着这个循环显然具有恒定的空间复杂性。的 O(1)

为了进一步澄清,我宁愿问你一个问题:

  

您是否将(迭代)二进制搜索称为O(n)空间复杂度算法?

绝对不是。

你的函数check_10()使用预分配的列表和哈希表(就像迭代二进制搜索使用预分配的排序数组)和2个常量空间变量,因此它具有O(1)空间复杂度。

PS :我正在澄清OP在此回答的评论中提出的疑问 - &gt;

正如MichaelRecachinas所指出的,此循环中的StringInteger是引用。它们不是副本,因此它们不会对此功能的空间复杂性做出任何贡献。

PPS Integer aString i只分配一次内存,然后在循环的每次迭代中重复使用。

答案 2 :(得分:0)

这具有O(n)空间复杂度,因为列表占用了该空间。 :)。哈希表最多是另一个O(n),因此空间复杂度之和仍为O(n)。