Как реализован контейнер multimap C++?

например, вектор C++ реализуется с использованием динамического массива, где каждый элемент использует последовательные пространства памяти.

Я знаю, что c++ multimap является отношением один ко многим, но какова внутренняя структура?

4 ответов


стандарт C++ не определяет, как должны быть реализованы стандартные контейнеры, он дает только определенные ограничения, такие как тот, который вы говорите для векторов.

multimaps имеют определенную сложность выполнения (O (lg n) для интересных операций) и другие гарантии и могут быть реализованы как красно-черные деревья. Вот как они реализованы в стандартной библиотеке GNU C++.


очень часто красно-черное дерево. См., например,красно-черные деревья STL от доктора Добба.


дополнение к" предпочтительному " ответу, потому что так не позволит мне прокомментировать:

учитывая ключ со значениями B, C, D, поведение итераторов намного проще реализовать, если каждый элемент имеет свой собственный узел. Find () определяется для возврата первого результата в серии, а последующая итерация проходит через остальные элементы. The де-факто разница между картой и multimap заключается в том, что multimap сортируется с помощью

исправление: стандарт C++11 явно указывает, что новые пары (ключ, отображение) вставляются в конце любых существующих значений, имеющих один и тот же ключ. Это поднимает вопрос, который я не рассматривал: может ли multimap содержать два узла, в которых ключ и сопоставленная цель одинаковы. Стандарт, похоже, не занимает четкой позиции по этому вопросу, но примечательно, что оператор сравнения не требуется для сопоставленного типа. Если вы напишете тестовую программу, то найдете что multimap может отображать X в 1, 2, 1. То есть: "1" может появляться несколько раз в качестве цели, и два экземпляра будут не быть объединены. Для некоторых алгоритмов это недостаток.

этой статьи от д-ра Доббса говорит о базовой реализации RB-tree, которая обычно используется. Главное отметить, что операция повторного баланса на самом деле не заботится о ключах вообще, поэтому вы можете построить RB-дерево, которое допускает дублирование ключи.


multimap так же, как это более простая версия i.e std:: map, в основном построен с использованием красных черных деревьев. Сам стандарт C++ не указывает реализацию. Но в большинстве случаев (я лично проверил SGI STL) используются красные черные деревья. Красные черные деревья-это деревья, сбалансированные по высоте, и поэтому операция выборки / чтения на них всегда гарантируется O(log (n)). Но если вам интересно, как хранятся значения ключа. каждый key->pair сохраняется как отдельный узел красный черный дерево ( хотя один и тот же ключ может появляться несколько раз, как и в случае ключа 'b' ниже). Ключ используется для поиска / поиска 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) Вставка выполняется с помощью оператора

компилятор, который я использовал, - gcc-5.1 (C++14).