我知道单个地图查询最多占用log(N)时间。但是我想知道,我已经看到很多使用字符串作为映射键的示例。例如,将std :: string作为键与地图而不是int相关联的性能成本是多少?
std::map<std::string, aClass*> someMap;
vs std::map<int, aClass*> someMap;
谢谢!
答案 0 :(得分:13)
分析渐近性能的算法正在研究必须执行的操作以及它们添加到等式中的成本。为此,您需要首先了解执行的操作是什么,然后评估其成本。
在平衡二叉树中搜索密钥(映射恰好是)需要O( log N )
复杂操作。这些操作中的每一个都意味着比较匹配的密钥并且如果密钥不匹配则遵循适当的指针(子)。这意味着总成本与这两项运营成本的log N
成比例。以下指针是一个恒定时间操作O(1)
,比较键取决于键。对于整数键,比较快O(1)
。比较两个字符串是另一个故事,它需要的时间与所涉及的字符串的大小成比例O(L)
(我故意使用L
作为字符串参数的长度而不是更常见的是N
。
当您将所有费用加起来时,您可以使用整数作为关键字,总费用为O( log N )*( O(1) + O(1) )
,相当于O( log N )
。 (O(1)
隐藏在O
符号默默隐藏的常量中。
如果您使用字符串作为键,则总费用为O( log N )*( O(L) + O(1) )
,其中常量时间操作被更昂贵的线性操作O(L)
隐藏,并且可以转换为O( L * log N )
。也就是说,在由字符串键入的地图中定位元素的成本与存储在地图中的元素数量的对数乘以用作键的字符串的平均长度成比例。
请注意,big-O表示法最适合用作分析工具来确定算法在问题规模增长时的行为方式,但它隐藏了许多对原始性能很重要的事实。
作为最简单的示例,如果将密钥从通用字符串更改为1000个字符的数组,则可以隐藏在符号中删除的常量中的成本。比较1000个字符的数组是一个常数操作,恰好需要相当长的时间。使用渐近符号只是O( log N )
操作,就像整数一样。
许多其他隐藏成本也会发生同样的情况,因为创建元素的成本通常被认为是一个恒定时间操作,只是因为它不依赖于您的问题的参数(定位块的成本)每个分配中的内存不依赖于您的数据集,而是依赖于算法分析范围之外的内存碎片,获取malloc内部锁定的成本,以保证不会有两个进程尝试返回相同的块内存取决于锁的争用,它依赖于处理器的数量,进程以及它们执行的内存请求数量......,再次超出了算法分析的范围)。在阅读大O符号的成本时,你必须意识到它的真正含义。
答案 1 :(得分:8)
除了比较已经提到的字符串的时间复杂性之外,每次将项添加到容器时,字符串键也会导致额外的内存分配。在某些情况下,例如高度并行的系统,全局分配器互斥体可能是性能问题的根源。
通常,您应该选择在您的情况下最有意义的替代方案,并且仅根据实际性能测试进行优化。众所周知,很难判断出什么是瓶颈。
答案 2 :(得分:1)
成本差异将与比较两个总数与比较两个字符串之间的成本差异相关联。
比较两个字符串时,必须取消引用指向第一个字符的指针,然后比较它们。如果它们相同,则必须比较第二个字符,依此类推。如果你的字符串有一个很长的公共前缀,这可能会使这个过程变慢。但是,它不太可能像比较一样快。
答案 3 :(得分:1)
成本当然可以在实际O(1)时间内比较整数,而在O(n)时间(n是最大共享前缀)中比较字符串。此外,字符串的存储消耗比整数的空间更多的空间。 除了这些明显的差异外,没有重大的性能成本。
答案 4 :(得分:0)
首先,我怀疑在实际的应用程序中,无论你有字符串键还是int键都会产生明显的差异。分析您的应用程序将告诉您它是否重要。
如果确实重要,您可以将密钥更改为此类(未经测试):
class Key {
public:
unsigned hash;
std::string s;
int cmp(const Key& other) {
int diff = hash - other.hash;
if (diff == 0)
diff = strcmp(s, other.s);
return diff;
}
现在你正在对两个字符串的哈希进行int比较。如果散列不同,则字符串肯定不同。如果哈希值相同,则仍需要比较字符串,因为Pigeonhole Principle。
答案 5 :(得分:0)
简单示例,只需访问具有相同数量键的两个映射中的值 - 一个int键相同int值的另一个字符串使用字符串的时间长8倍。