Ricerca…
Osservazioni
Per usare qualsiasi di
std::map
ostd::multimap
dovrebbe essere incluso il file di intestazione<map>
.std::map
estd::multimap
mantengono i loro elementi ordinati secondo l'ordine crescente delle chiavi. In caso distd::multimap
, non avviene alcun ordinamento per i valori della stessa chiave.La differenza fondamentale tra
std::map
estd::multimap
è chestd::map
one non consente valori duplicati per la stessa chiave dovestd::multimap
fa.Le mappe sono implementate come alberi di ricerca binari. Quindi
search()
,insert()
,erase()
richiede Θ (log n) tempo medio. Per il funzionamento a tempo costante usastd::unordered_map
.size()
funzionisize()
eempty()
hanno Θ (1) complessità temporale, il numero di nodi è memorizzato nella cache per evitare di camminare attraverso l'albero ogni volta che vengono chiamate queste funzioni.
Accesso agli elementi
Una coppia std::map
accetta (key, value)
come input.
Considera il seguente esempio di inizializzazione di std::map
:
std::map < std::string, int > ranking { std::make_pair("stackoverflow", 2),
std::make_pair("docs-beta", 1) };
In una std::map
, gli elementi possono essere inseriti come segue:
ranking["stackoverflow"]=2;
ranking["docs-beta"]=1;
Nell'esempio precedente, se lo stackoverflow
della chiave è già presente, il suo valore verrà aggiornato a 2. Se non è già presente, verrà creata una nuova voce.
In una std::map
, è possibile accedere direttamente agli elementi dando la chiave come indice:
std::cout << ranking[ "stackoverflow" ] << std::endl;
Si noti che l'utilizzo operator[]
sulla mappa in realtà inserirà un nuovo valore con la chiave interrogata nella mappa. Ciò significa che non puoi usarlo su una const std::map
, anche se la chiave è già memorizzata nella mappa. Per impedire questo inserimento, controlla se l'elemento esiste (per esempio usando find()
) o usa at()
come descritto di seguito.
Gli elementi di una std::map
sono accessibili con at()
:
std::cout << ranking.at("stackoverflow") << std::endl;
Nota che at()
verrà std::out_of_range
un'eccezione std::out_of_range
se il contenitore non contiene l'elemento richiesto.
In entrambi i contenitori std::map
e std::multimap
, è possibile accedere agli elementi utilizzando gli iteratori:
// 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"
Inizializzazione di std :: map o std :: multimap
std::map
e std::multimap
possono essere inizializzati fornendo coppie chiave-valore separate da virgola. Le coppie valore-chiave possono essere fornite da {key, value}
o possono essere create esplicitamente da std::make_pair(key, value)
. Poiché std::map
non consente chiavi duplicate e l'operatore virgola va da destra a sinistra, la coppia a destra verrebbe sovrascritta con la coppia con lo stesso tasto a sinistra.
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
Entrambi possono essere inizializzati con iteratore.
// 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}
Eliminazione di elementi
Rimozione di tutti gli elementi:
std::multimap< int , int > mmp{ {1, 2}, {3, 4}, {6, 5}, {8, 9}, {3, 4}, {6, 7} };
mmp.clear(); //empty multimap
Rimozione di elementi da qualche parte con l'aiuto di iteratore:
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}
Rimozione di tutti gli elementi in un intervallo:
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}
Rimozione di tutti gli elementi con un valore fornito come chiave:
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}
Rimozione di elementi che soddisfano un predicato pred
:
std::map<int,int> m;
auto it = m.begin();
while (it != m.end())
{
if (pred(*it))
it = m.erase(it);
else
++it;
}
Inserimento di elementi
Un elemento può essere inserito in una std::map
solo se la sua chiave non è già presente nella mappa. Dato ad esempio:
std::map< std::string, size_t > fruits_count;
Una coppia chiave-valore viene inserita in una
std::map
attraverso la funzione membroinsert()
. Richiede unapair
come argomento: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));
La funzione
insert()
restituisce unapair
composta da un iteratore e un valorebool
:- Se l'inserimento ha avuto successo, l'iteratore punta all'elemento appena inserito e il valore
bool
ètrue
. - Se esisteva già un elemento con la stessa
key
, l'inserimento fallisce. Quando ciò accade, l'iteratore punta all'elemento che causa il conflitto e il valorebool
èfalse
.
Il seguente metodo può essere utilizzato per combinare operazioni di inserimento e ricerca:
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 }
- Se l'inserimento ha avuto successo, l'iteratore punta all'elemento appena inserito e il valore
Per comodità, il contenitore
std::map
fornisce all'operatore subscript l'accesso agli elementi nella mappa e l'inserimento di nuovi se non esistono:fruits_count["apple"] = 10;
Benché più semplice, impedisce all'utente di verificare se l'elemento esiste già. Se manca un elemento,
std::map::operator[]
lo crea implicitamente, inizializzandolo con il costruttore predefinito prima di sovrascriverlo con il valore fornito.
insert()
può essere usato per aggiungere più elementi contemporaneamente usando una lista di coppie rinforzate. Questa versione di insert () restituisce void:fruits_count.insert({{"apricot", 1}, {"jackfruit", 1}, {"lime", 1}, {"mango", 7}});
insert()
può anche essere usato per aggiungere elementi usando gli iteratori che denotano i valori di inizio e fine divalue_type
:std::map< std::string, size_t > fruit_list{ {"lemon", 0}, {"olive", 0}, {"plum", 0}}; fruits_count.insert(fruit_list.begin(), fruit_list.end());
Esempio:
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
}
}
La complessità temporale per un'operazione di inserimento è O (log n) perché std::map
è implementata come alberi.
Una pair
può essere costruita esplicitamente usando make_pair()
ed emplace()
:
std::map< std::string , int > runs;
runs.emplace("Babe Ruth", 714);
runs.insert(make_pair("Barry Bonds", 762));
Se sappiamo dove verrà inserito il nuovo elemento, possiamo usare emplace_hint()
per specificare un hint
iteratore. Se il nuovo elemento può essere inserito prima del hint
, l'inserimento può essere eseguito in tempo costante. Altrimenti si comporta allo stesso modo di 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);
Iterating su std :: map o std :: multimap
std::map
o std::multimap
potrebbe essere attraversato dai seguenti modi:
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
Durante l'iterazione su std::map
o std::multimap
, l'uso di auto
è preferito per evitare inutili conversioni implicite (vedere questa risposta per ulteriori dettagli).
Ricerca in std :: map o in std :: multimap
Esistono diversi modi per cercare una chiave in std::map
o in std::multimap
.
Per ottenere l'iteratore della prima occorrenza di una chiave, è possibile utilizzare la funzione
find()
. Restituisceend()
se la chiave non esiste.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.
Un altro modo per scoprire se esiste una voce in
std::map
o instd::multimap
sta utilizzando la funzionecount()
, che conta quanti valori sono associati a una determinata chiave. Poichéstd::map
associa un solo valore a ogni chiave, la sua funzionecount()
può solo restituire 0 (se la chiave non è presente) o 1 (se lo è). Perstd::multimap
,count()
può restituire valori maggiori di 1 poiché possono essere presenti più valori associati alla stessa chiave.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;
Se ti interessa solo che alcuni elementi esistano,
find
è strettamente migliore: documenta il tuo intento e, permultimaps
, può fermarsi una volta trovato il primo elemento corrispondente.Nel caso di
std::multimap
, potrebbero esserci più elementi con la stessa chiave. Per ottenere questo intervallo, viene utilizzata la funzioneequal_range()
che restituiscestd::pair
con l'estremo inferiore di iteratore (incluso) e il limite superiore (esclusivo) rispettivamente. Se la chiave non esiste, entrambi gli iteratori puntano aend()
.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
Controllo del numero di elementi
Il contenitore std::map
ha una funzione membro empty()
, che restituisce true
o false
, a seconda che la mappa sia vuota o meno. La funzione membro size()
restituisce il numero di elementi memorizzati in un contenitore 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;
}
Tipi di mappe
Mappa normale
Una mappa è un contenitore associativo, contenente coppie chiave-valore.
#include <string>
#include <map>
std::map<std::string, size_t> fruits_count;
Nell'esempio precedente, std::string
è il tipo di chiave e size_t
è un valore .
La chiave agisce come un indice nella mappa. Ogni chiave deve essere unica e deve essere ordinata.
Se hai bisogno di più elementi con la stessa chiave, considera l'utilizzo di
multimap
(spiegato sotto)Se il tuo tipo di valore non specifica alcun ordine, o se desideri sovrascrivere l'ordine predefinito, puoi fornire uno:
#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;
Se il comparatore
StrLess
restituiscefalse
per due chiavi, vengono considerate uguali anche se i loro contenuti effettivi differiscono.
Multi-Map
Multimap consente di memorizzare più coppie di valori-chiave con lo stesso tasto nella mappa. Altrimenti, la sua interfaccia e la sua creazione sono molto simili alla normale mappa.
#include <string>
#include <map>
std::multimap<std::string, size_t> fruits_count;
std::multimap<std::string, size_t, StrLess> fruits_count2;
Hash-Map (Mappa non ordinata)
Una mappa hash memorizza coppie chiave-valore simili a una mappa normale. Tuttavia, non ordina gli elementi rispetto alla chiave. Invece, viene utilizzato un valore hash per la chiave per accedere rapidamente alle coppie chiave-valore necessarie.
#include <string>
#include <unordered_map>
std::unordered_map<std::string, size_t> fruits_count;
Le mappe non ordinate sono in genere più veloci, ma gli elementi non sono memorizzati in alcun ordine prevedibile. Ad esempio, l'iterazione di tutti gli elementi in una mappa unordered_map
fornisce gli elementi in un ordine apparentemente casuale.
Creazione di std :: map con tipi definiti dall'utente come chiave
Per poter utilizzare una classe come chiave in una mappa, tutto ciò che è richiesto dalla chiave è che sia copiable
e assignable
. L'ordinamento all'interno della mappa è definito dal terzo argomento del modello (e dall'argomento del costruttore, se usato). Il valore predefinito è std::less<KeyType>
, che per impostazione predefinita è <
operatore, ma non è necessario utilizzare i valori predefiniti. Basta scrivere un operatore di confronto (preferibilmente come un oggetto funzionale):
struct CmpMyType
{
bool operator()( MyType const& lhs, MyType const& rhs ) const
{
// ...
}
};
In C ++, il predicato "confronta" deve essere un severo ordinamento debole . In particolare, compare(X,X)
deve restituire false
per qualsiasi X
cioè se CmpMyType()(a, b)
restituisce true, quindi CmpMyType()(b, a)
deve restituire false e, se entrambi restituiscono false, gli elementi sono considerati uguali (membri della stessa classe di equivalenza).
Ordinamento debole rigoroso
Questo è un termine matematico per definire una relazione tra due oggetti.
La sua definizione è:
Due oggetti xey sono equivalenti se sia f (x, y) che f (y, x) sono falsi. Si noti che un oggetto è sempre (dall'invarianza dell'invarianza) equivalente a se stesso.
In termini di C ++ ciò significa che se si hanno due oggetti di un determinato tipo, si dovrebbero restituire i seguenti valori rispetto all'operatore <.
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
Il modo in cui definisci l'equivalente / meno dipende totalmente dal tipo di oggetto.