下面的方法“getValue”解析一个String,根据String构建一个Map并返回一个与该键相关联的值。 以下方法的性能是“getValue”O(n)的平方吗?
我基于此,因为每次添加新的键值字符串时,都需要对其进行解析,然后将该项添加到Map中。
import java.util.Arrays;
import java.util.List;
import java.util.Map;
public class MeasureBigO {
private static final String testString = "keyTest=keyValue";
public static void main(String args[]){
System.out.println(getValue("keyTest"));
}
private static String getValue(String key){
Map<String, String> config = new java.util.HashMap<String, String>();
List<String> configItems = Arrays.asList(testString.split(","));
for (String configItem : configItems) {
configItem = configItem.trim();
List<String> keyValuesPairs = Arrays.asList(configItem.split("="));
try {
config.put(keyValuesPairs.get(0).trim(), keyValuesPairs.get(1).trim());
}
catch(IndexOutOfBoundsException ioobe){
return null;
}
}
return config.get(key);
}
}
答案 0 :(得分:5)
此算法的复杂性为O(n)
,其中n
是configItems
中的项目数。
testString
:可以在O(1)
时间内拆分。configItems
的循环与O(n)
成比例。split
的其他configItem
操作也是O(1)
。除了他们之外,你的循环中还有说明,所以在实践中它是C * O(n)
,其中C
是一些不变的成本。
答案 1 :(得分:2)
将字符串拆分为列表项将相对于字符串的大小为O(n)。
将这些项目拆分成对将是O(n * m),相对于对的数量(n,它与先前的源字符串的长度有关)和每个字符串的大小(m)。在实践中,我们可以假设每个字符串的大小与对的数量相比可以忽略不计。
相对于对的数量,将这些添加到散列映射将是O(n)。 假设高于正常的哈希冲突没有问题,检索该值大约为O(1)。
由于这里的所有n
都与同一事物有关,我们有O(n)+ O(n * m)+ O(n)+ O(1)。当我们在计算复杂度的顺序时忽略低阶,这将是O(n * m)。但是,如上所述,我们希望m
扫描能够找到可忽略不计的关键,特别是下面提到的修复,所以我们只说O(n)。
另一种看待这种情况的方法是第一次操作需要c₀ * n + k₀
,其中c 0是每个项目完成的恒定工作量,而k 0是整个操作的恒定工作量。接下来需要c₁ * n * m + k₁
,下一个c₂ * n + k₂
和最终查找c₃ * 1
。添加k₄
作为方法本身的常量开销,我们有:
c₀ * n + k₀ + c₁ * n * m + k₁ + c₂ * n + k₂ + c₃ * 1 + k₄
等于:
(c₁ * n * m) + (c₀ + c₂) * n + c₃ + k₀ + k₁ + k₂ + k₄
如果我们可以忽略n * m
,那么删除较低的订单会给我们一个与n
或m
成比例的时间。因此,O(n * m)或O(n)取决于我们是否可以忽略每个键值对的大小。
这里值得注意的两件事。第一个是将configItem.split("=")
的调用替换为configItem.split("=", 2)
,这样可以提高正确性(捕获值包含=
的情况),并使m
与大小成比例关键而不是整对,让我们更有信心假设它可以忽略不计(事实上,在提供轻微的性能提升方面)。
第二个是假设键值对的源集没有改变,那么我们可以在设置时创建哈希映射,或者在第一次调用方法时创建哈希映射,然后再次使用它后续电话。然后,这将使方法O(1)具有初始O(n)设置。
相反,如果源集确实发生了变化,那么根本就没有必要使用地图,只要在迭代对中找到匹配的密钥就可以立即返回值;因此采用O(n)方法,但常数较小,平均只检查成对匹配的一半。