【发布时间】:2011-06-06 21:44:15
【问题描述】:
例如,C++ 向量是使用动态数组实现的,其中每个元素使用连续的内存空间。
我知道 C++ 多重映射是一对多的关系,但内部结构是什么?
【问题讨论】:
例如,C++ 向量是使用动态数组实现的,其中每个元素使用连续的内存空间。
我知道 C++ 多重映射是一对多的关系,但内部结构是什么?
【问题讨论】:
C++ 标准没有定义标准容器应该如何实现,它只给出了某些约束,就像你对向量所说的那样。
multimaps 具有一定的运行时复杂度(O(lg n) 用于有趣的操作)和其他保证,并且可以实现为red-black trees。这就是它们在 GNU 标准 C++ 库中的实现方式。
【讨论】:
很多时候,red-black tree。参见例如STL's Red-Black Trees Dobb 博士的。
【讨论】:
除了“首选”答案之外,因为 SO 不允许我发表评论:
给定一个值为 B、C、D 的键,如果每个元素都有自己的节点,迭代器的行为会更容易实现。 Find() 被定义为返回系列中的第一个结果,随后的迭代将带您穿过剩余的元素。 事实上 map 和 multimap 的区别在于 multimap 使用
更正:C++11 标准明确指出,新的(键、映射)对将插入到具有相同键的任何现有值的末尾。这提出了一个我没有考虑过的问题:多重映射是否可以包含两个节点,其中键和映射的目标都相同。该标准似乎对此没有明确的立场,但值得注意的是映射类型不需要比较运算符。如果你写一个测试程序,你会发现一个multimap可以将X映射到1、2、1。即:“1”可以作为目标多次出现,两个实例不会合并。对于一些算法来说这是一个不足。
来自 Dobbs 博士的 article 讨论了常用的底层 rb-tree 实现。需要注意的主要一点是,重新平衡操作实际上根本不关心键,这就是为什么您可以构建一个允许重复键的 rb-tree。
【讨论】:
multimap 就像它的简单版本一样,即 std::map,主要是使用红黑树构建的。 C++ 标准本身并没有指定实现。但在大多数情况下(我亲自检查过 SGI STL)都使用红黑树。红黑树是高度平衡的树,因此对它们的获取/读取操作始终保证为 O(log(n)) 时间。但是,如果您想知道键的值是如何存储的。每个key->pair 都保存为红黑树中的一个单独节点(即使同一个键可能会出现多次,就像下面的键'b' 一样)。密钥用于查找/搜索 rb-tree。找到键后,将返回其存储在节点中的值。
std::multimap<char,int> mmp;
mmp.insert(std::pair<char,int>('a',10));
mmp.insert(std::pair<char,int>('b',20));
mmp.insert(std::pair<char,int>('b',10));
mmp.insert(std::pair<char,int>('b',15));
mmp.insert(std::pair<char,int>('b',20));
mmp.insert(std::pair<char,int>('c',25));
mmp.insert(std::pair<char,int>('a',15));
mmp.insert(std::pair<char,int>('a',7));
for (std::multimap<char,int>::iterator it=mmp.begin(); it!=mmp.end(); ++it){
std::cout << (*it).first << " => " << (*it).second << " . Address of (*it).second = " << &((*it).second) << '\n';
}
输出:
a => 10 . Address of (*it).second = 0x96cca24
a => 15 . Address of (*it).second = 0x96ccae4
a => 7 . Address of (*it).second = 0x96ccb04
b => 20 . Address of (*it).second = 0x96cca44
b => 10 . Address of (*it).second = 0x96cca64
b => 15 . Address of (*it).second = 0x96cca84
b => 20 . Address of (*it).second = 0x96ccaa4
c => 25 . Address of (*it).second = 0x96ccac4
最初我认为像 'b' 这样的单个键的值可能存储在 std::vector 中。
template <class K, class V>
struct Node {
K key;
std::vector<V> values;
struct Node* left;
struct Node* right;
}
但后来我意识到这会违反 O(log(n)) 的保证获取时间。此外,打印出值的地址可以确认具有公共键的值是不连续的。
它们的键是使用运算符
所以如果我们先插入 (键 = 'b',值 = 20) 进而 (键 = 'b',值 = 10) 插入是使用 operator
我使用的编译器是gcc-5.1 (C++14)。
【讨论】:
std::pair 中。而且使用时间戳并不是一个好主意,序列号的开销更小,而且通常效果更好。