使用std::map和std::string键与int键的成本比较?

21
我知道每个映射查询最多需要log(N)的时间。然而我想知道,我看到很多使用字符串作为映射键的例子。相比于使用整数作为键,将std::string用作键的性能成本是多少呢? std::map<std::string, aClass*> someMap; vs std::map<int, aClass*> someMap; 谢谢!

写一个小测试应该很容易。然而,整数始终比字符串快,而且可能快得多。 - anon
2
下一个问题:如果您将地图更改为使用int而不是字符串,那么自己进行转换会损失多少性能? - David Thornley
1
@David:这取决于很多因素,但可能会相当大。每次插入和搜索操作的额外成本为O(L)(L:字符串大小),但这仅在整个操作中执行一次:O(L)+O(log N),将是O(L)O(log N)中较大的一个。如果键保留为字符串,则在所有节点中执行比较,并且成本为O(L)*O(log N),即O(L*log N) - David Rodríguez - dribeas
6个回答

18

分析算法的渐进性能是在考虑必须执行的操作以及它们增加到方程式中的成本。为此,您需要首先了解执行的操作,然后评估其成本。

在平衡二叉树(其映射恰好是如此)中搜索关键字需要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)。即,以字符串作为键的映射中定位元素的成本与存储在映射中的元素数量的对数成比例,乘以用作键的字符串的平均长度。

请注意,大O符号最适合用作分析工具,以确定问题规模增长时算法的行为方式,但它隐藏了许多重要的原始性能细节。

作为最简单的例子,如果你将键从通用字符串更改为1000个字符的数组,则可以在符号中省略该成本。比较1000个字符的数组是一个常数操作,但需要相当长的时间。在渐近符号表示法中,这只是一个 O(log N) 操作,就像整数一样。

许多其他隐藏成本也会发生同样的情况,如元素创建成本通常被认为是一个常数时间操作,因为它不依赖于问题的参数(在每个分配中定位内存块的成本不取决于数据集,而是取决于算法分析范围之外的内存碎片化状态;在 malloc 内部获取锁的成本以确保没有两个进程尝试返回相同的内存块取决于锁的争用,该争用又取决于处理器数量、进程数量以及它们执行的内存请求量...再次超出了算法分析的范围)。在阅读大O符号时,必须意识到它真正代表的含义。


字符串比较在最坏情况下可能是O(N),但平均情况通常要好得多。实际上,对于两个随机字符串,它的时间复杂度是O(1)! - MSalters

14
除了比较字符串所带来的时间复杂度外,每次向容器添加一个字符串键也会导致额外的内存分配。在某些情况下,例如高度并行的系统中,全局分配器互斥锁可能成为性能问题的源头。
通常情况下,你应该选择在你的情况下最有意义的替代方案,并且只基于实际性能测试进行优化。判断什么将成为瓶颈是出了名的困难。

1

首先,我怀疑在实际应用中,使用字符串键或整数键是否会有任何明显的区别。对您的应用程序进行分析将告诉您它是否重要。

如果确实很重要,您可以将键更改为类似于以下内容(未经测试):

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;
}

现在你正在对两个字符串的哈希值进行整数比较。如果哈希值不同,那么这两个字符串肯定不同。如果哈希值相同,你仍然需要比较这两个字符串,因为涉及到鸽巢原理


1

成本差异将与比较两个整数与比较两个字符串之间的成本差异相关联。

当比较两个字符串时,您必须取消引用指针以获取第一个字符并进行比较。如果它们相同,则必须比较第二个字符,依此类推。如果您的字符串具有长公共前缀,则可能会稍微减慢该过程。但是,与比较整数相比,这很不可能快。


1
当然,int可以在实际的O(1)时间内进行比较,而字符串则需要O(n)时间(n是最大共享前缀)。此外,字符串的存储消耗的空间比整数要多。 除了这些明显的差异之外,没有主要性能损失。

0

一个简单的例子,只涉及访问两个具有相同键数的映射中的值 - 一个是整数键,另一个是相同整数值的字符串,使用字符串需要8倍的时间。


你能提供这些数字的参考或示例吗? - Shafik Yaghmour

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接