C++
std :: mapa
Szukaj…
Uwagi
Aby użyć dowolnej ze
std::map
lubstd::multimap
należy dołączyć plik nagłówka<map>
.std::map
istd::multimap
utrzymują swoje elementy posortowane zgodnie z rosnącą kolejnością klawiszy. W przypadkustd::multimap
nie występuje sortowanie według wartości tego samego klucza.Podstawowa różnica między
std::map
istd::multimap
polega na tym, żestd::map
one nie zezwala na powielanie wartości dla tego samego klucza, co robistd::multimap
.Mapy są implementowane jako drzewa wyszukiwania binarnego. Tak więc
search()
,insert()
,erase()
zajmuje średnio Θ (log n) czas. Do pracy w trybie ciągłym używajstd::unordered_map
.Funkcje
size()
iempty()
mają złożoność czasową Θ (1), liczba węzłów jest buforowana, aby uniknąć przechodzenia przez drzewo przy każdym wywołaniu tych funkcji.
Dostęp do elementów
std::map
przyjmuje jako dane wejściowe pary (key, value)
.
Rozważ następujący przykład inicjalizacji std::map
:
std::map < std::string, int > ranking { std::make_pair("stackoverflow", 2),
std::make_pair("docs-beta", 1) };
W std::map
elementy można wstawiać w następujący sposób:
ranking["stackoverflow"]=2;
ranking["docs-beta"]=1;
W powyższym przykładzie, jeśli przepełnienie stackoverflow
kluczy jest już obecne, jego wartość zostanie zaktualizowana do 2. Jeśli nie jest już obecny, zostanie utworzony nowy wpis.
W std::map
elementy można uzyskać bezpośrednio, podając klucz jako indeks:
std::cout << ranking[ "stackoverflow" ] << std::endl;
Zauważ, że użycie operator[]
na mapie spowoduje wstawienie nowej wartości z pytanym kluczem do mapy. Oznacza to, że nie można go używać na const std::map
, nawet jeśli klucz jest już zapisany na mapie. Aby temu zapobiec, sprawdź, czy element istnieje (na przykład za pomocą find()
) lub użyj at()
jak opisano poniżej.
Elementy std::map
są dostępne za pomocą at()
:
std::cout << ranking.at("stackoverflow") << std::endl;
Zauważ, że at()
wyrzuci wyjątek std::out_of_range
jeśli kontener nie zawiera żądanego elementu.
W obu kontenerach std::map
i std::multimap
dostęp do elementów można uzyskać za pomocą iteratorów:
// Example using begin()
std::multimap < int, std::string > mmp { std::make_pair(2, "stackoverflow"),
std::make_pair(1, "docs-beta"),
std::make_pair(2, "stackexchange") };
auto it = mmp.begin();
std::cout << it->first << " : " << it->second << std::endl; // Output: "1 : docs-beta"
it++;
std::cout << it->first << " : " << it->second << std::endl; // Output: "2 : stackoverflow"
it++;
std::cout << it->first << " : " << it->second << std::endl; // Output: "2 : stackexchange"
// Example using rbegin()
std::map < int, std::string > mp { std::make_pair(2, "stackoverflow"),
std::make_pair(1, "docs-beta"),
std::make_pair(2, "stackexchange") };
auto it2 = mp.rbegin();
std::cout << it2->first << " : " << it2->second << std::endl; // Output: "2 : stackoverflow"
it2++;
std::cout << it2->first << " : " << it2->second << std::endl; // Output: "1 : docs-beta"
Inicjalizacja std :: map lub std :: multimap
Zarówno std::map
i std::multimap
można zainicjować, podając pary klucz-wartość oddzielone przecinkiem. Pary klucz-wartość mogą być dostarczone przez {key, value}
lub mogą być jawnie utworzone przez std::make_pair(key, value)
. Ponieważ std::map
nie zezwala na duplikowanie kluczy, a operator przecinków wykonuje od prawej do lewej, para po prawej stronie zostanie zastąpiona parą z tym samym kluczem po lewej stronie.
std::multimap < int, std::string > mmp { std::make_pair(2, "stackoverflow"),
std::make_pair(1, "docs-beta"),
std::make_pair(2, "stackexchange") };
// 1 docs-beta
// 2 stackoverflow
// 2 stackexchange
std::map < int, std::string > mp { std::make_pair(2, "stackoverflow"),
std::make_pair(1, "docs-beta"),
std::make_pair(2, "stackexchange") };
// 1 docs-beta
// 2 stackoverflow
Oba można zainicjować za pomocą iteratora.
// From std::map or std::multimap iterator
std::multimap< int , int > mmp{ {1, 2}, {3, 4}, {6, 5}, {8, 9}, {6, 8}, {3, 4},
{6, 7} };
// {1, 2}, {3, 4}, {3, 4}, {6, 5}, {6, 8}, {6, 7}, {8, 9}
auto it = mmp.begin();
std::advance(it,3); //moved cursor on first {6, 5}
std::map< int, int > mp(it, mmp.end()); // {6, 5}, {8, 9}
//From std::pair array
std::pair< int, int > arr[10];
arr[0] = {1, 3};
arr[1] = {1, 5};
arr[2] = {2, 5};
arr[3] = {0, 1};
std::map< int, int > mp(arr,arr+4); //{0 , 1}, {1, 3}, {2, 5}
//From std::vector of std::pair
std::vector< std::pair<int, int> > v{ {1, 5}, {5, 1}, {3, 6}, {3, 2} };
std::multimap< int, int > mp(v.begin(), v.end());
// {1, 5}, {3, 6}, {3, 2}, {5, 1}
Usuwanie elementów
Usuwanie wszystkich elementów:
std::multimap< int , int > mmp{ {1, 2}, {3, 4}, {6, 5}, {8, 9}, {3, 4}, {6, 7} };
mmp.clear(); //empty multimap
Usuwanie skądś elementu za pomocą iteratora:
std::multimap< int , int > mmp{ {1, 2}, {3, 4}, {6, 5}, {8, 9}, {3, 4}, {6, 7} };
// {1, 2}, {3, 4}, {3, 4}, {6, 5}, {6, 7}, {8, 9}
auto it = mmp.begin();
std::advance(it,3); // moved cursor on first {6, 5}
mmp.erase(it); // {1, 2}, {3, 4}, {3, 4}, {6, 7}, {8, 9}
Usuwanie wszystkich elementów z zakresu:
std::multimap< int , int > mmp{ {1, 2}, {3, 4}, {6, 5}, {8, 9}, {3, 4}, {6, 7} };
// {1, 2}, {3, 4}, {3, 4}, {6, 5}, {6, 7}, {8, 9}
auto it = mmp.begin();
auto it2 = it;
it++; //moved first cursor on first {3, 4}
std::advance(it2,3); //moved second cursor on first {6, 5}
mmp.erase(it,it2); // {1, 2}, {6, 5}, {6, 7}, {8, 9}
Usuwanie wszystkich elementów mających podaną wartość jako klucz:
std::multimap< int , int > mmp{ {1, 2}, {3, 4}, {6, 5}, {8, 9}, {3, 4}, {6, 7} };
// {1, 2}, {3, 4}, {3, 4}, {6, 5}, {6, 7}, {8, 9}
mmp.erase(6); // {1, 2}, {3, 4}, {3, 4}, {8, 9}
Usuwanie elementów spełniających predykat pred
:
std::map<int,int> m;
auto it = m.begin();
while (it != m.end())
{
if (pred(*it))
it = m.erase(it);
else
++it;
}
Wstawianie elementów
Element można wstawić do std::map
tylko wtedy, gdy jego klucz nie jest już obecny na mapie. Biorąc na przykład:
std::map< std::string, size_t > fruits_count;
Para klucz-wartość jest wstawiana do
std::map
poprzez funkcję członkainsert()
. Wymagapair
jako argumentu:fruits_count.insert({"grapes", 20}); fruits_count.insert(make_pair("orange", 30)); fruits_count.insert(pair<std::string, size_t>("banana", 40)); fruits_count.insert(map<std::string, size_t>::value_type("cherry", 50));
Funkcja
insert()
zwracapair
składającą się z iteratora i wartościbool
:- Jeśli wstawienie zakończyło się pomyślnie, iterator wskazuje na nowo wstawiony element, a wartość
bool
jesttrue
. - Jeśli był już element z tym samym
key
, wstawianie kończy się niepowodzeniem. Kiedy tak się dzieje, iterator wskazuje element powodujący konflikt, abool
ma wartośćfalse
.
Aby połączyć operację wstawiania i wyszukiwania, można użyć następującej metody:
auto success = fruits_count.insert({"grapes", 20}); if (!success.second) { // we already have 'grapes' in the map success.first->second += 20; // access the iterator to update the value }
- Jeśli wstawienie zakończyło się pomyślnie, iterator wskazuje na nowo wstawiony element, a wartość
Dla wygody
std::map
zapewnia operatorowi indeksu dolnego dostęp do elementów na mapie i wstawianie nowych, jeśli nie istnieją:fruits_count["apple"] = 10;
Chociaż jest to prostsze, uniemożliwia użytkownikowi sprawdzenie, czy element już istnieje. Jeśli brakuje elementu,
std::map::operator[]
domyślnie go tworzy, inicjując go z domyślnym konstruktorem przed nadpisaniem go podaną wartością.
insert()
może być użyty do dodania kilku elementów jednocześnie za pomocą stężonej listy par. Ta wersja insert () zwraca void:fruits_count.insert({{"apricot", 1}, {"jackfruit", 1}, {"lime", 1}, {"mango", 7}});
insert()
można również używać do dodawania elementów za pomocą iteratorów oznaczających początek i koniec wartościvalue_type
:std::map< std::string, size_t > fruit_list{ {"lemon", 0}, {"olive", 0}, {"plum", 0}}; fruits_count.insert(fruit_list.begin(), fruit_list.end());
Przykład:
std::map<std::string, size_t> fruits_count;
std::string fruit;
while(std::cin >> fruit){
// insert an element with 'fruit' as key and '1' as value
// (if the key is already stored in fruits_count, insert does nothing)
auto ret = fruits_count.insert({fruit, 1});
if(!ret.second){ // 'fruit' is already in the map
++ret.first->second; // increment the counter
}
}
Złożoność czasowa operacji wstawiania wynosi O (log n), ponieważ std::map
są implementowane jako drzewa.
pair
może być skonstruowana jawnie za pomocą make_pair()
i emplace()
:
std::map< std::string , int > runs;
runs.emplace("Babe Ruth", 714);
runs.insert(make_pair("Barry Bonds", 762));
Jeśli wiemy, gdzie zostanie wstawiony nowy element, wówczas możemy użyć emplace_hint()
aby określić hint
iteratora. Jeśli nowy element można wstawić tuż przed hint
, wstawianie może być wykonane w stałym czasie. W przeciwnym razie zachowuje się tak samo jak emplace()
:
std::map< std::string , int > runs;
auto it = runs.emplace("Barry Bonds", 762); // get iterator to the inserted element
// the next element will be before "Barry Bonds", so it is inserted before 'it'
runs.emplace_hint(it, "Babe Ruth", 714);
Iterowanie po std :: map lub std :: multimap
std::map
lub std::multimap
można przeglądać w następujący sposób:
std::multimap< int , int > mmp{ {1, 2}, {3, 4}, {6, 5}, {8, 9}, {3, 4}, {6, 7} };
//Range based loop - since C++11
for(const auto &x: mmp)
std::cout<< x.first <<":"<< x.second << std::endl;
//Forward iterator for loop: it would loop through first element to last element
//it will be a std::map< int, int >::iterator
for (auto it = mmp.begin(); it != mmp.end(); ++it)
std::cout<< it->first <<":"<< it->second << std::endl; //Do something with iterator
//Backward iterator for loop: it would loop through last element to first element
//it will be a std::map< int, int >::reverse_iterator
for (auto it = mmp.rbegin(); it != mmp.rend(); ++it)
std::cout<< it->first <<" "<< it->second << std::endl; //Do something with iterator
Podczas iteracji po std::map
lub std::multimap
, preferowane jest użycie auto
aby uniknąć niepotrzebnych niejawnych konwersji (więcej informacji można znaleźć w tej odpowiedzi SO ).
Wyszukiwanie w std :: map lub w std :: multimap
Istnieje kilka sposobów wyszukiwania klucza w std::map
lub w std::multimap
.
Aby uzyskać iterator pierwszego wystąpienia klucza, można użyć funkcji
find()
. Zwracaend()
jeśli klucz nie istnieje.std::multimap< int , int > mmp{ {1, 2}, {3, 4}, {6, 5}, {8, 9}, {3, 4}, {6, 7} }; auto it = mmp.find(6); if(it!=mmp.end()) std::cout << it->first << ", " << it->second << std::endl; //prints: 6, 5 else std::cout << "Value does not exist!" << std::endl; it = mmp.find(66); if(it!=mmp.end()) std::cout << it->first << ", " << it->second << std::endl; else std::cout << "Value does not exist!" << std::endl; // This line would be executed.
Innym sposobem sprawdzenia, czy istnieje wpis w
std::map
lub wstd::multimap
jest użycie funkcjicount()
, która zlicza, ile wartości jest powiązanych z danym kluczem. Ponieważstd::map
kojarzy tylko jedną wartość z każdym klawiszem, jego funkcjacount()
może zwrócić tylko 0 (jeśli klucz nie jest obecny) lub 1 (jeśli jest). W przypadkustd::multimap
count()
może zwracać wartości większe niż 1, ponieważ z tym samym kluczem może być powiązanych kilka wartości.std::map< int , int > mp{ {1, 2}, {3, 4}, {6, 5}, {8, 9}, {3, 4}, {6, 7} }; if(mp.count(3) > 0) // 3 exists as a key in map std::cout << "The key exists!" << std::endl; // This line would be executed. else std::cout << "The key does not exist!" << std::endl;
Jeśli zależy ci tylko na tym, czy istnieje jakiś element,
find
jest zdecydowanie lepszy: dokumentuje twoje zamiary, a dlamultimaps
może zatrzymać się po znalezieniu pierwszego pasującego elementu.W przypadku
std::multimap
może istnieć kilka elementów mających ten sam klucz. Aby uzyskać ten zakres, używana jest funkcjaequal_range()
która zwraca odpowiedniostd::pair
z iteratorem odpowiednio dolną granicę (włącznie) i górną granicę (wyłącznie). Jeśli klucz nie istnieje, oba iteratory wskazują naend()
.auto eqr = mmp.equal_range(6); auto st = eqr.first, en = eqr.second; for(auto it = st; it != en; ++it){ std::cout << it->first << ", " << it->second << std::endl; } // prints: 6, 5 // 6, 7
Sprawdzanie liczby elementów
Kontener std::map
ma funkcję empty()
, która zwraca true
lub false
, w zależności od tego, czy mapa jest pusta, czy nie. Funkcja członkowska size()
zwraca liczbę elementów przechowywanych w std::map
:
std::map<std::string , int> rank {{"facebook.com", 1} ,{"google.com", 2}, {"youtube.com", 3}};
if(!rank.empty()){
std::cout << "Number of elements in the rank map: " << rank.size() << std::endl;
}
else{
std::cout << "The rank map is empty" << std::endl;
}
Rodzaje map
Zwykła mapa
Mapa to kontener asocjacyjny zawierający pary klucz-wartość.
#include <string>
#include <map>
std::map<std::string, size_t> fruits_count;
W powyższym przykładzie std::string
jest typem klucza , a size_t
jest wartością .
Klucz działa jak indeks na mapie. Każdy klucz musi być unikalny i musi zostać zamówiony.
Jeśli potrzebujesz wielu elementów z tym samym kluczem, rozważ użycie
multimap
(wyjaśnione poniżej)Jeśli twój typ wartości nie określa żadnego zamówienia lub chcesz zastąpić domyślne zamówienie, możesz podać jeden:
#include <string> #include <map> #include <cstring> struct StrLess { bool operator()(const std::string& a, const std::string& b) { return strncmp(a.c_str(), b.c_str(), 8)<0; //compare only up to 8 first characters } } std::map<std::string, size_t, StrLess> fruits_count2;
Jeśli komparator
StrLess
zwraca wartośćfalse
dla dwóch kluczy, są one uważane za takie same, nawet jeśli ich rzeczywista zawartość różni się.
Wiele map
Multimapa pozwala na przechowywanie wielu par klucz-wartość z tym samym kluczem na mapie. W przeciwnym razie jego interfejs i tworzenie jest bardzo podobne do zwykłej mapy.
#include <string>
#include <map>
std::multimap<std::string, size_t> fruits_count;
std::multimap<std::string, size_t, StrLess> fruits_count2;
Mapa hash (mapa nieuporządkowana)
Mapa skrótów przechowuje pary klucz-wartość podobne do zwykłej mapy. Nie porządkuje jednak elementów w odniesieniu do klucza. Zamiast tego używana jest wartość skrótu dla klucza, aby szybko uzyskać dostęp do potrzebnych par klucz-wartość.
#include <string>
#include <unordered_map>
std::unordered_map<std::string, size_t> fruits_count;
Nieuporządkowane mapy są zwykle szybsze, ale elementy nie są przechowywane w żadnej przewidywalnej kolejności. Na przykład iteracja po wszystkich elementach w unordered_map
daje elementy w pozornie losowej kolejności.
Tworzenie std :: map z typami zdefiniowanymi przez użytkownika jako klucz
Aby móc użyć klasy jako klucza na mapie, wszystko, czego wymaga się od klucza, to możliwość copiable
i assignable
. Kolejność na mapie jest definiowana przez trzeci argument szablonu (i argument konstruktora, jeśli jest używany). Domyślnie jest to std::less<KeyType>
, domyślnie jest to operator <
, ale nie ma wymogu używania wartości domyślnych. Po prostu napisz operator porównania (najlepiej jako obiekt funkcjonalny):
struct CmpMyType
{
bool operator()( MyType const& lhs, MyType const& rhs ) const
{
// ...
}
};
W C ++ predykat „porównaj” musi mieć ścisłą słabą kolejność . W szczególności compare(X,X)
musi zwrócić false
dla dowolnego X
tzn. jeśli CmpMyType()(a, b)
zwraca true, to CmpMyType()(b, a)
musi zwracać false, a jeśli oba zwracają false, elementy są uważane za równe (członkowie tej samej klasy równoważności).
Ściśle słabe zamawianie
Jest to termin matematyczny służący do zdefiniowania relacji między dwoma obiektami.
Jego definicja to:
Dwa obiekty xiy są równoważne, jeśli oba f (x, y) i f (y, x) są fałszywe. Zauważ, że obiekt jest zawsze (przez niezmienniczość nierefleksyjną) sam w sobie.
W kategoriach C ++ oznacza to, że jeśli masz dwa obiekty danego typu, powinieneś zwrócić następujące wartości w porównaniu z operatorem <.
X a;
X b;
Condition: Test: Result
a is equivalent to b: a < b false
a is equivalent to b b < a false
a is less than b a < b true
a is less than b b < a false
b is less than a a < b false
b is less than a b < a true
To, jak zdefiniujesz ekwiwalent / mniej, zależy całkowicie od rodzaju twojego obiektu.