C++多映射容器是如何实现的

How is the C++ multimap container implemented?

本文关键字:实现 何实现 映射 C++      更新时间:2023-10-16

例如,C++向量使用动态数组实现,其中每个元素使用连续的内存空间。

我知道C++多映射是一对多的关系,但内部结构是什么?

C++标准没有定义标准容器应该如何实现,它只给出了某些约束,比如你说的向量约束。

multimaps具有一定的运行时复杂性(对于感兴趣的操作为O(lgn))和其他保证,并且可以实现为红黑树。这就是它们在GNU标准C++库中的实现方式。

通常情况下,一个红黑树。参见Dr.Dobb的STL的红黑树。

添加到"首选"答案,因为SO不让我评论:

给定一个值为B、C、D的键,如果每个元素都有自己的节点,迭代器的行为就会更容易实现。Find()被定义为返回序列中的第一个结果,随后的迭代将遍历其余元素。映射和多映射之间的事实上的区别在于多映射使用<在整个value_type上,其中映射使用<仅通过key_type

更正:C++11标准明确规定,在具有相同密钥的任何现有值的末尾插入新的(密钥、映射)对。这引发了一个我没有考虑过的问题:多映射是否可以包含两个节点,其中键和映射的目标都相同。标准似乎对此没有明确的立场,但值得注意的是,映射类型上不需要比较运算符。如果你编写一个测试程序,你会发现多映射可以将X映射到1,2,1。也就是说:"1"可以多次作为目标出现,并且这两个实例将不会合并。对于某些算法来说,这是一个缺陷。

Dobbs博士的这篇文章讨论了常用的底层rb树实现。需要注意的主要一点是,重新平衡操作实际上根本不关心密钥,这就是为什么您可以构建一个允许重复密钥的rb树。

多映射就像它的简单版本一样,即std::map,主要是使用红黑树构建的。C++标准本身并没有指定实现。但在大多数情况下(我亲自检查了SGI STL)都使用红黑树。红黑树是高度平衡的树,因此对它们的获取/读取操作总是保证为O(log(n))时间。但若您想知道密钥的值是如何存储的。每个CCD_ 1被保存为红黑树中的单独节点(即使同一密钥可能像下面的密钥CCD_。关键字用于查找/搜索rb树。一旦找到密钥,就会返回存储在节点中的值。

  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)插入是使用运算符<由于第二个"b"不小于第一个插入的"b",因此它被插入到"二叉树的右分支"中。

我使用的编译器是gcc-5.1(C++14)。