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)?
答案 0 :(得分:7)
空间复杂性问“我在这段代码中使用了多少额外空间(渐近,说话)”。以下是空间复杂度分析的工作原理,显示了两个一般情况(对于您的代码片段):
hashtable
和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);
}
}
}
假设N
中有hashtable
个元素且没有删除任何元素(即所有a <= 10
元素都为N
),在循环结束时,您将N
中剩余hashtable
个元素。此外,String
中N
个键中的每个hashtable
都包含最多S
个字符。最后,Integer
中N
值中的每个hashtable
都是常量。
同样,M
中的字符串数量可能为list
,其中每个String
最多可包含S
个字符。
最后,Integer a
对分析没有贡献,因为它引用了已经占用的内存。我们仍然可以考虑这个Integer a
常量记忆。
因此,假设在方法中声明了hashtable
和list
,那么您正在考虑O(N*S + M*S + I)
的空间复杂度。
也就是说,渐渐地,我们并不关心I
(Integer a
),因为它的大小可能比N
和M
小得多。同样,S
可能比N
和M
小得多。这意味着空间复杂度为O(N + M)
。由于两者都是线性项,我们可以(小心地)将其减少到O(n)
,其中n
是一个线性项,是N and M
的线性组合。
hashtable
和list
或传递(如您的示例中所示)// 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);
}
}
}
在此方法中,list
和hashtable
已在其他位置分配,这意味着此方法的空间复杂度为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()
个元素。因此,无论它是否在内存中重用相同或不同的点,它们都会导致线性空间复杂性。
在您的特定情况下,list
和n
已经在程序的其他位置分配,并且未在此处介绍,因此它们不会导致复杂性,{{1} }和list
在内存中只是常量。因此,此方法将为hashtable
。
答案 1 :(得分:1)
除了2个常量变量string i
和Integer a
之外,此方法不会分配任何额外空间。这意味着这个循环显然具有恒定的空间复杂性。的 O(1)强>
为了进一步澄清,我宁愿问你一个问题:
您是否将(迭代)二进制搜索称为O(n)空间复杂度算法?
绝对不是。
你的函数check_10()使用预分配的列表和哈希表(就像迭代二进制搜索使用预分配的排序数组)和2个常量空间变量,因此它具有O(1)空间复杂度。
PS :我正在澄清OP在此回答的评论中提出的疑问 - &gt;
正如MichaelRecachinas所指出的,此循环中的String
和Integer
是引用。它们不是副本,因此它们不会对此功能的空间复杂性做出任何贡献。
PPS :Integer a
和String i
只分配一次内存,然后在循环的每次迭代中重复使用。
答案 2 :(得分:0)
这具有O(n)空间复杂度,因为列表占用了该空间。 :)。哈希表最多是另一个O(n),因此空间复杂度之和仍为O(n)。