如果它只是ASCII字符,我只使用大小为256的bool数组。但是Unicode有很多字符。 维基百科说,unicode有超过110000个字符。那么bool [110000]可能不是个好主意? 2.假设这些字符正在流中,我只想在检测到重复时停止。我该怎么做呢? 由于集合太大,我在想哈希表。但是我怎么知道碰撞发生的时间,因为一旦检测到碰撞我不想继续。有没有办法在哈希表的STL实现中执行此操作?
在速度和内存利用率方面高效。
答案 0 :(得分:1)
有几种可能的解决方案:
bool[0x110000]
(请注意,这是一个十六进制常量,而不是问题中的十进制常量)
vector<bool>
,尺寸为0x110000
对包含每个遇到的代码点的vector<uint32_t>
或list<uint32_t>
进行排序
map<uint32_t, bool>
或unordered_map<uint32_t, bool>
包含代码点映射到是否遇到过
set<uint32_t>
或unordered_set<uint32_t>
包含遇到的每个代码点
自定义容器,例如a bloom filter为这类问题提供高密度概率存储
现在,让我们对6种变体进行基本分析:
确切地要求0x110000
字节= 1.0625 MiB加上单个分配的开销。设置和测试都非常快。
虽然这似乎是完全相同的解决方案,但它只需要大约1/8的内存,因为它会将bool存储在一个位中而不是一个每个字节。设置和测试都非常快,相对于第一个解决方案的性能可能更好或更差,这取决于cpu缓存大小,内存性能以及测试数据等。
虽然可能占用最少的内存(每个遇到的代码点需要4个字节,因此只要输入流最多包含0x110000 / 8/4 = 34816,它就需要更少的内存),此解决方案的性能将为非常糟糕:测试需要O(log(n))用于向量(二进制搜索)和O(n)用于列表(二进制搜索需要随机访问),而插入需要O(n)用于向量(所有后续元素必须移动)和O(1)列表(假设您保留了测试失败的结果)。这意味着,测试+插入周期需要O(n)。因此,您的总运行时间将为O(n ^ 2)...
如果不进行大量讨论,很明显我们不需要bool
,而只是测试存在,导致解决方案5.
两个集的性能非常相似,set
通常使用二叉树实现,unordered_set
使用哈希映射。这意味着两者都是相当低效的内存:两者都包含额外的开销(树中的非叶节点和包含散列图中的散列的实际表)意味着它们可能每个条目需要8-16个字节。测试和插入是set
的O(log(n))和unordered_set
的摊销的O(1)。
要回答评论,请测试uint32 const x
中是否包含unordered_set<uint32_t> data
,如下所示:if(data.count(x))
或if(data.find(x) != data.end())
。
这里的主要缺点是开发商必须投入大量的工作。此外,作为示例给出的布隆过滤器是概率数据结构,意味着可能存在误报(在这种特定情况下不会出现假阴性)。
由于您的测试数据不是实际的文本数据,因此使用任何一种类型的实际上都非常低效(很有可能)。由于这也是实现比天真解决方案1和2更好的性能的唯一可能性,因此它很可能也会显着变慢。
考虑到它更容易处理并且你似乎对内存消耗的相当意识这一事实,最终的结论是vector<bool>
似乎是最合适的解决方案。
Unicode旨在表示文本。这意味着您的测试用例以及随后的任何分析都可能存在严重缺陷。
此外,虽然检测重复的代码点很简单,但字符的概念更加模糊,可能需要某种规范化(例如“ä”) “可以是one codepoint,也可以是”a“和diacritical mark,甚至可以基于cyrillic "a"。)