繁体   English   中英

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

[英]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);
        }
    }
}

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

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

示例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);
        }
    }
}

假设您在hashtableN元素并且没有删除任何元素(即,对于所有N元素, a <= 10 ),在循环终止时,您将在hashtable保留N元素。 此外, hashtableN键中的每个String最多包含S字符。 最后, hashtable N值中的每个Integer都是常量。

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

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

因此,假设在方法中声明了hashtablelist ,您将看到O(N*S + M*S + I)的空间复杂度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)因为我们只在Integer aString i使用常量空间(尽管从技术上讲,它们是对先前的引用)分配内存 - 您可以考虑存储引用的常量空间)。

但是不是每次重复使用记忆点O(1)?

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

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

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

但是,一般情况下,如果您指的是在每次迭代中重复使用内存中的相同位置listhashtable ,答案就更简单了。 考虑以下:

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

即使list被声明一次并且我们只是遍历list并打印内容, foo()仍然是内存复杂度的O(n),因为我们已经分配了list ,其中渐近情况下最多可以有n元素。 因此,无论它是否在内存中重用相同或不同的点,它们都仍然有助于线性空间复杂性。

TL;博士

但是,在您的特定情况下, listhashtable都已经在程序的其他地方分配,并且未在此处介绍,因此它们不会导致复杂性,并且Integer aString i仅在内存中保持不变。 因此,这种方法将是O(1)

是O(1)

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

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

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

绝对不。

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

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

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

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

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

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM