我正在写一个算法,我在那里寻找成对的值,这些值在加在一起时会产生另一个我正在寻找的值。
我发现使用Map
会使我的算法从O(n²)加速。我后来意识到我并没有真正使用Map
中包含的值,因此List
就足够了。
我在Google上进行了强力搜索,但是在我的问题标题中没有找到关于这些方法的渐近运行时间的任何信息。
你能指出我应该在哪里寻找这些信息吗?
答案 0 :(得分:47)
list.contains
是O(n),而map.containsKey
对于散列图是O(1),对于树图是O(logn)。
对于哈希映射,谷歌搜索哈希表。对于树形图,谷歌搜索二叉树或类似的。维基百科在这些主题上有很好的条目。
如果您不需要地图,可以使用相应的设置。在内部,它们是根据相应的地图实现的,其中值只是一些虚拟单例对象。
但要小心避免上课Hashtable
。这是现代图书馆的考古文物。对于您的情况,HashSet
可能是最佳选择。
答案 1 :(得分:6)
Map
和List
是接口,因此没有关于其实现及其性能的信息。但是,如果您使用最新的实现(LinkedList
为ArrayList
,List
为HashMap
,Map
为contains()
,则必须使用HashMap
方法,在最坏的情况下,浏览整个列表,并将您的元素与每个条目进行比较。这是一个O(n)操作。
如果使用HashMap
,则实现完全不同:i
包含一个数组,其中的条目数多于其中的元素(实际上,数组大小介于4n / 3之间3n / 2表示地图中的n个元素)。它计算键的哈希值,它是一个int,并将它包装在0和你的数组大小之间(假设这个数字是i
)。然后它将元素放在数组的索引i+1
(或i+2
,containsKey
...如果已经采用了以前的索引)。因此,当您使用i
检查密钥在线状态时,它会重新计算哈希值和i
值,并检查i+1
,i
...索引,直到它为止找到一个空的数组单元格。理论上,你可以有一个O(n)最坏的情况,如果数组几乎已满,所有的键都具有几乎相同的contains
值,但是如果有一个好的散列函数,你就有一个常数时间{{1 }和get
函数。 (但是,如果您不需要调整数组大小,添加元素很快,真的慢 - 我认为您需要重新计算每个键的索引。)
因此,如果您需要检查集合中的键外观,并且不需要保留顺序(这里有SortedHashMap
,但我不知道它的性能),那么地图真的会更快,但它需要更多的记忆。
此外,如果您不需要键值,可以使用HashSet
(内部与HashMap
相同)。
答案 2 :(得分:0)
Map.containsKey()考虑到你正在使用HashMap,因为在HashMap中搜索是在O(1)中完成的。
List.contains()通常应采用顺序搜索或二进制搜索,因此复杂性至少为O(log n)
答案 3 :(得分:0)
HashSet似乎更快:
还请注意,通常不需要在HashMap和HashSet上调用.contains(),但我将其保留在代码上以更准确地回答您的问题:
long t = System.currentTimeMillis();
HashMap<String, Boolean> map = new HashMap<>();
for (int i = 0; i < 10000; i++) {
String s = (Math.random() * 100) + "";
if (!map.containsKey(s)) {
map.put(s, true);
}
}
System.out.println("HashMap: " + (System.currentTimeMillis() - t));
t = System.currentTimeMillis();
ArrayList<String> list = new ArrayList<>();
for (int i = 0; i < 10000; i++) {
String s = (Math.random() * 100) + "";
if (!list.contains(s)) {
list.add(s);
}
}
System.out.println("ArrayList: " + (System.currentTimeMillis() - t));
t = System.currentTimeMillis();
HashSet<String> set = new HashSet<>();
for (int i = 0; i < 10000; i++) {
String s = (Math.random() * 100) + "";
if (!set.contains(s)) {
set.add(s);
}
}
System.out.println("HashSet: " + (System.currentTimeMillis() - t));