Uso específico de mapa y conjunto [C++]

contenedor asociativo

Los contenedores asociativos almacenan pares clave-valor de estructuras <clave, valor>, que son más eficientes que los contenedores secuenciales en la recuperación de datos. Por ejemplo: conjunto, mapa, conjunto_desordenado, mapa_desordenado, etc.

Nota : pila, cola y prioridad_queue en C++STL pertenecen a adaptadores de contenedor, y los contenedores básicos que utilizan de forma predeterminada son deque, deque y vector respectivamente.

par clave-valor

Los pares clave-valor se utilizan para representar una estructura con una correspondencia uno a uno . Esta estructura generalmente contiene solo dos variables miembro, clave y valor. La clave representa el valor de la clave y el valor representa la información correspondiente a la clave.

Por ejemplo, si crea un diccionario inglés-chino, existe una correspondencia uno a uno entre las palabras en inglés en el diccionario y sus significados chinos correspondientes, es decir, puede encontrar los significados chinos correspondientes a través de las palabras.

La definición de pares clave-valor en SGI-STL es la siguiente:

template <class T1, class T2>
struct pair
{
    
    
	typedef T1 first_type;
	typedef T2 second_type;
	T1 first;
	T2 second;
	pair() 
	: first(T1())
	, second(T2())
	{
    
    
	}
	pair(const T1& a, const T2& b) 
	: first(a)
	, second(b)
	{
    
    
	}
};

colocar

1. Un conjunto es un contenedor que almacena elementos en un orden determinado. Al utilizar el iterador del conjunto para recorrer los elementos del conjunto, se puede obtener una secuencia ordenada.

2. Los valores de los elementos almacenados en el conjunto son únicos y no se pueden repetir, por lo que puede utilizar el conjunto para eliminar duplicados.

3. A diferencia de mapa/multimapa, mapa/multimapa almacena pares clave-valor reales <clave, valor>. Solo el valor se coloca en el conjunto, pero el par clave-valor compuesto por <valor, valor> en realidad se almacena en la parte inferior. , al insertar elementos en el contenedor establecido, solo necesita insertar el valor y no es necesario construir un par clave-valor.

4. Los elementos del conjunto no se pueden modificar. El conjunto se implementa mediante un árbol de búsqueda binario en la parte inferior . Si se modifica el valor de un nodo en el árbol de búsqueda binario, entonces el árbol ya no será un árbol de búsqueda binario. árbol de búsqueda

5. Internamente, los elementos de un conjunto siempre se clasifican según criterios de clasificación débiles estrictos y específicos indicados por sus objetos de comparación internos. Cuando no se pasa ningún objeto de comparación interna, los elementos del conjunto se comparan de forma predeterminada como menos que

6. El acceso del contenedor establecido a un solo elemento a través de la clave suele ser más lento que el contenedor unordered_set, pero el contenedor establecido permite la iteración directa de elementos según el orden.

7. El conjunto se implementa utilizando un árbol de búsqueda equilibrado (árbol rojo-negro) en el nivel inferior, por lo que la complejidad temporal de encontrar un elemento en el conjunto es logN

Cómo se define el conjunto

//构造int类型的空容器
set<int> s1; 

//拷贝构造int类型s1
set<int> s2(s1); 
string str("abcdef");

//拷贝string
set<char> s3(str.begin(), str.end()); 

//构造int类型的空容器,比较方式指定为大于
set < int, greater<int>> s4; 

uso del conjunto

void Testset()
{
    
    
	 //去重
	set<int> s;
	s.insert(1);
	s.insert(4);
	s.insert(3);
	s.insert(3);
	s.insert(2);
	s.insert(2);
	s.insert(3);
	//遍历方式一

	for (auto e : s)
	{
    
    
		cout << e << " ";
	}
	cout << endl;
	//删除方式一
	s.erase(3);
	//遍历方式二

	set<int>::iterator it = s.begin();
	while (it != s.end())
	{
    
    
		cout << *it << " ";
		it++;
	}
	cout << endl;
	//删除方式二, 正向迭代器遍历 
	set<int>::iterator pos = s.find(1);
	if (pos!=s.end())
	{
    
    
		s.erase(pos);
	}
	//遍历方式三
	set<int>::reverse_iterator rit = s.rbegin();
	while (rit != s.rend())
	{
    
    
		cout << *rit << " ";
		rit++;
	}
	cout << endl;
	//容器中值为2的个数 
	cout<<s.count(2);
	cout << s.size();
	s.clear();
	cout << s.empty();

}

void TestmultiSet()
{
    
    
	//可以重复 
	multiset<int> m;
	m.insert(3);
	m.insert(5);
	m.insert(8);
	m.insert(7);
	m.insert(7);
	m.insert(9);
	m.insert(7);
	for (auto e : m)
	{
    
    
		cout << e << "";
	}
	//find 
	//set<int> s;
	//s.insert(1);
	//s.insert(4);
	//s.insert(3);
	//s.insert(3);
	//s.insert(2);
	//s.insert(2);
	//s.insert(3);
	//if (s.find(3) != s.end())
	//{
    
    
	//	cout << "找到了" << " ";
	//}
	//else
	//{
    
    
	//	cout << "找不到" << " ";
	//}
	auto pos = m.find(7);//返回中序中第一个7
	while (pos != m.end())
	{
    
    
		cout << *pos << " ";
		pos++;
	}
	cout << endl;
	cout << m.count(7) << endl;
	auto ret = m.equal_range(17);
	auto itlow = ret.first;
	auto itup = ret.second;
	//[itlow , itup) 左闭右开 左边界是第一个7,右边界是比7大的,才能完全删除所有的7
	cout << *itlow<<endl;
	cout << *itup<<endl;
	m.erase(itlow, itup);//?
	for (auto e : m)
	{
    
    
		cout << e << " ";
	}
	cout << endl;
	
}
void Testset2()
{
    
    
	set<int> s;
	s.insert(1);
	s.insert(4);
	s.insert(3);
	s.insert(3);
	s.insert(2);
	s.insert(2);
	s.insert(3);
	//交换两个容器的数据 
	set<int> tmp{
    
     11,22,33,44 };
	s.swap(tmp);
	for (auto e : s)
	{
    
    
		cout << e << " ";
	}
	cout << endl;
}

int main()
{
    
    
Testset();
TestmultiSet();
Testset2();
return 0 ;
}

conjunto múltiple

La implementación subyacente del contenedor multiconjunto y el contenedor de conjuntos es la misma, ambos son árboles de búsqueda equilibrados (árboles rojo-negro). En segundo lugar, las interfaces de las funciones miembro proporcionadas por el contenedor multiconjunto y el contenedor de conjuntos son básicamente las mismas. La única diferencia entre el contenedor de conjuntos múltiples y el contenedor de conjuntos es que el conjunto múltiple permite la redundancia de valores clave, es decir, los elementos almacenados en el contenedor de conjuntos múltiples se pueden repetir.

#include <iostream>
#include <set>
using namespace std;

int main()
{
    
    
	multiset<int> ms;
	//插入元素(允许重复)
	ms.insert(1);
	ms.insert(4);
	ms.insert(3);
	ms.insert(3);
	ms.insert(2);
	ms.insert(2);
	ms.insert(3);
	for (auto e : ms)
	{
    
    
		cout << e << " ";
	}
	cout << endl; //1 2 2 3 3 3 4
	return 0;
}

Insertar descripción de la imagen aquí

mapa

1. Map es un contenedor asociativo. Almacena elementos que constan de clave y valor en un orden específico (comparados por clave). Usando el iterador de map para recorrer los elementos en el mapa, se puede obtener una secuencia ordenada.

2. En un mapa, la clave de valor clave se usa generalmente para ordenar e identificar elementos de forma única, mientras que el valor de valor almacena el contenido asociado con esta clave de valor clave. Los tipos de clave y valor pueden ser diferentes, y dentro del mapa, la clave y el valor están unidos a través del tipo de miembro tipo_valor y tienen un alias como par.

3. El valor clave del elemento en el contenedor del mapa no se puede modificar, pero el valor del elemento se puede modificar, porque el árbol de búsqueda binario subyacente del mapa se construye en función del valor clave de cada elemento, no del valor.

4. Internamente, los elementos del mapa siempre se comparan y clasifican según el valor clave. Cuando no se pasa ningún objeto de comparación interna, los valores clave de los elementos en el mapa se comparan de forma predeterminada siendo menores que.

5. El contenedor de mapas es generalmente más lento que el contenedor unordered_map para acceder a un solo elemento a través del valor clave, pero el contenedor de mapas permite la iteración directa de elementos según el orden.

6. El contenedor del mapa admite el operador de acceso a subíndice, es decir, al ingresar la clave en [], puede encontrar el valor correspondiente a la clave.

7. El mapa se implementa utilizando un árbol de búsqueda equilibrado (árbol rojo-negro) en el nivel inferior, por lo que la complejidad temporal de encontrar un elemento en el mapa es logN

Cómo se define el mapa

map<int, double> m1; //构造一个key为int类型,value为double类型的空容器

map<int, double> m2(m1); //拷贝构造key为int类型,value为double类型的m1容器的复制品

map<int, double> m3(m2.begin(), m2.end()); //使用迭代器拷贝构造m2容器某段区间的复制品

map<int, double, greater<int>> m4; //构造一个key为int类型,value为double类型的空容器,key比较方式指定为大于

insertar

Insertar descripción de la imagen aquí

pair<iterator,bool> insert (const value_type& val);

Tipo value_type, de hecho value_type es un alias de tipo par:

typedef pair<const Key, T> value_type;

Al insertar un elemento, debe construir un objeto par con clave y valor, y luego pasar el objeto par como parámetro a la función de inserción.

Método 1: objeto anónimo

#include <iostream>
#include <string>
#include <map>
using namespace std;

int main()
{
    
    
	map<int, string> m;
	//方式一:调用pair的构造函数,构造一个匿名对象插入
	m.insert(pair<int, string>(2, "two"));
	m.insert(pair<int, string>(1, "one"));
	m.insert(pair<int, string>(3, "three"));
	for (auto e : m)
	{
    
    
		cout << "<" << e.first << "," << e.second << ">" << " ";
	}
	cout << endl; //<1,one> <2,two> <3,three>
	return 0;
}

Método 2: llamar a la inserción de plantilla de función make_pair (de uso común)

La biblioteca proporciona las siguientes plantillas de funciones make_pair:

template <class T1, class T2>
pair<T1, T2> make_pair(T1 x, T2 y)
{
    
    
	return (pair<T1, T2>(x, y));
}

Pase la clave y el valor a la función make_pair. La plantilla de función deducirá automática e implícitamente el tipo de los parámetros pasados ​​y finalmente construirá y devolverá un objeto de par correspondiente.

#include <iostream>
#include <string>
#include <map>
using namespace std;

int main()
{
    
    
	map<int, string> m;
	//方式二:调用函数模板make_pair,构造对象插入
	m.insert(make_pair(2, "two"));
	m.insert(make_pair(1, "one"));
	m.insert(make_pair(3, "three"));
	for (auto e : m)
	{
    
    
		cout << "<" << e.first << "," << e.second << ">" << " ";
	}
	cout << endl; //<1,one> <2,two> <3,three>
	return 0;
}

El valor de retorno de la función de inserción
Insertar descripción de la imagen aquí
resume el contenido del documento:
el valor de retorno de la función de inserción también es un objeto de par. El tipo del primer miembro del objeto de par es el tipo de mapa iterador y el tipo del segundo El miembro es de tipo bool y su significado específico es el siguiente:

1. Si el valor clave del elemento a insertar no existe en el mapa, la función de inserción se inserta correctamente y devuelve el iterador y verdadero del elemento insertado.
2. Si el valor clave del elemento a insertar ya existe en el mapa, la función de inserción no logra insertar y devuelve el iterador y falso del elemento cuyo valor clave es clave en el mapa.

encontrar

Insertar descripción de la imagen aquí

iterator find (const key_type& k);

Busque en el mapa de acuerdo con el valor clave dado. Si se encuentra, se devuelve el iterador del elemento correspondiente. Si no se encuentra, se devuelve el iterador directo de la siguiente posición del último elemento en el contenedor.

#include <iostream>
#include <string>
#include <map>
using namespace std;

int main()
{
    
    
	map<int, string> m;
	m.insert(make_pair(2, "two"));
	m.insert(make_pair(1, "one"));
	m.insert(make_pair(3, "three"));
	//获取key值为2的元素的迭代器
	map<int, string>::iterator pos = m.find(2);
	if (pos != m.end())
	{
    
    
		cout << pos->second << endl; //two
	}
	return 0;
}

borrar

Insertar descripción de la imagen aquí
Elimine el elemento especificado según el valor clave, o elimine el elemento especificado según el iterador. Si el elemento se elimina según el valor clave, se devuelve el número de elementos realmente eliminados.

#include <iostream>
#include <string>
#include <map>
using namespace std;

int main()
{
    
    
	map<int, string> m;
	m.insert(make_pair(2, "two"));
	m.insert(make_pair(1, "one"));
	m.insert(make_pair(3, "three"));
	for (auto kv : m)
	{
    
    
		cout << kv.first<<" " << kv.second;
	}
	cout << endl;
	//根据key值进行删除
	m.erase(3);
	for ( auto kv : m)
	{
    
    
		cout << kv.first << " " << kv.second;
	}
	//根据迭代器删除 
	map<int, string>::iterator pos = m.find(2);
	while (pos != m.end())
	{
    
    
		m.erase(pos);
	}
	for (auto kv : m)
	{
    
    
		cout << kv.first << " " << kv.second;
	}
	return 0;
}

[] sobrecarga del operador

Insertar descripción de la imagen aquí
Insertar descripción de la imagen aquí
El parámetro de la función sobrecargada del operador [] es un valor clave y el valor de retorno de esta función es el siguiente:

(* (  (this->insert(  make_pair(k, mapped_type() ) ) ).first)   ).second

Según el código anterior, se puede inferir que [], la lógica de implementación de la sobrecarga del operador:

mapped_type& operator[] (const key_type& k)
{
    
    
	//1、调用insert函数插入键值对
	pair<iterator, bool> ret = insert( make_pair(k, mapped_type() ) );
	//2、拿出从insert函数获取到的迭代器
	iterator it = ret.first;
	//3、返回该迭代器位置元素的值value
	return it->second;
}

Uso del operador []

int main()
{
    
    
		 map<string, string> dict;
		 dict.insert(make_pair("string", "字符串"));
		 dict.insert(make_pair("sort", "排序"));
		 dict.insert(make_pair("insert", "插入"));
	
		 cout << dict["sort"] << endl;//查找和读
		 dict["map"];//插入
		 dict["map"] = "映射,地图";//修改
		 dict["insert"] = "xxx";//修改
		 dict["set"] = "集合";//插入+修改
}

Para resumir:
1. Si k no está en el mapa, inserte primero el par clave-valor <k, V()> y luego devuelva la referencia al objeto V en el par clave-valor.
2. Si k ya está en el mapa, devuelve la referencia del objeto V correspondiente al elemento con valor clave k.

recorrido del iterador del mapa

int main()
{
    
    
	map<int, string>  m;
	m.insert(make_pair(2, "two"));
	m.insert(make_pair(1, "one"));
	m.insert(make_pair(3, "three"));

	//正向迭代器 
	map<int, string>::iterator   it = m.begin();
	while (it != m.end())
	{
    
    
		//cout << (*it).first << ":"<<(*it).second<<endl;//迭代器重载operator*
		cout << it->first << ":" << it->second << endl;//迭代器重载operator->
		it++;	
	}
	cout << endl;
	//反向迭代器 
	map<int, string>::reverse_iterator rit = m.rbegin();
	while (rit != m.rend())
	{
    
    
		cout << " " << rit->first << " " << rit->second;
		rit++;
	}
	cout << endl;
	//范围for ,kv就是*it
	for (auto kv : m)
	{
    
    
		cout << kv.first <<" " << kv.second;
	}
	return 0;
}

multimapa

La implementación subyacente de los contenedores multimapa es la misma que la de los contenedores de mapas. También son árboles de búsqueda equilibrados (árboles rojo-negro). La diferencia entre contenedores multimapa y contenedores de mapas es la misma que la diferencia entre contenedores multiconjunto y contenedores de conjuntos. permite la redundancia de valores clave, es decir, en contenedores multimapa Los elementos almacenados se pueden repetir.

#include <iostream>
#include <string>
#include <map>
using namespace std;

int main()
{
    
    
	multimap<int, string> mm;
	//插入元素(允许重复)
	mm.insert(make_pair(2, "two"));
	mm.insert(make_pair(2, "double"));
	mm.insert(make_pair(1, "one"));
	mm.insert(make_pair(3, "three"));
	for (auto e : mm)
	{
    
    
		cout << "<" << e.first << "," << e.second << ">" << " ";
	}
	cout << endl; //<1,one> <2,two> <2,double> <3,three>
	return 0;
}

Insertar descripción de la imagen aquí
Si crees que este artículo te resulta útil, también puedes darle me gusta, recopilarlo y volver a publicarlo con los dedos y prestarle mucha atención a Hud. ¡Cada
apoyo tuyo se convertirá en una motivación para seguir adelante! !

Supongo que te gusta

Origin blog.csdn.net/qq_73478334/article/details/133214906
Recomendado
Clasificación