[C++] El uso de unordered_map y unordered_set

Directorio de artículos


prefacio

Contenedores asociativos de series desordenadas :
En C++98 , STL proporciona una serie de contenedores asociativos cuya capa inferior es una estructura de árbol rojo-negro, y la eficiencia de la consulta puede llegar a O(logN), es decir, en el peor de los casos, es necesario comparar la altura del árbol rojo-negro Cuando el árbol Cuando hay muchos nodos en , la eficiencia de la consulta no es ideal. La mejor consulta es encontrar elementos con una pequeña cantidad de comparaciones, por lo que en C++11 , STL proporciona 4 más
Contenedores asociativos de la serie desordenada, estos cuatro contenedores son básicamente similares a los contenedores asociativos de la estructura de árbol rojo-negro, excepto que
Su estructura subyacente es diferente.
1.unordered_map

 Comparemos la diferencia entre unordered_map y map:

Al ver esto, todos han descubierto que, de hecho, sus funciones son exactamente las mismas, pero la capa inferior es diferente. Nota: el mapa y el conjunto usan iteradores bidireccionales, las series desordenadas usan iteradores unidireccionales

mapa / conjunto: la capa inferior es un árbol rojo-negro

unordered_map / unordered_set: la capa inferior es una tabla hash

¿Por qué diseñarlos a ambos? ¡Porque la eficiencia de búsqueda de hash es muy alta!

Vamos a la parte de uso:


1. El uso de unordered_map

#include <unordered_map>
#include <unordered_set>
#include <iostream>
using namespace std;

void unordered_map_test()
{
	unordered_map<string, int> ump;
	ump.insert(make_pair("left", 1));
	ump.insert(make_pair("right", 2));
	ump.insert(make_pair("string", 3));
	ump.insert(make_pair("list", 4));
	ump.insert(make_pair("list", 5));
	for (auto& e : ump)
	{
		cout << e.first << ":" << e.second << endl;
	}
	cout << endl;
	unordered_map<string, int>::iterator it = ump.begin();
	while (it != ump.end())
	{
		cout << it->first << ":" << it->second << endl;
		++it;
	}
}
int main()
{
	unordered_map_test();
}

 Podemos ver que el uso de unordered es exactamente el mismo que el de map. Los datos repetidos no se insertarán. Por supuesto, también hay series unordered_multimap que admiten la inserción de datos repetidos. Entonces, ¿cuál es la principal diferencia? Vamos a ver:

 Se puede ver que la principal diferencia es que la serie desordenada está desordenada, a continuación damos las pruebas de desempeño del mapa y la serie desordenada:

void unordered_map_test2()
{
	const size_t N = 1000000;

	unordered_set<int> us;
	set<int> s;

	vector<int> v;
	v.reserve(N);
	srand(time(0));
	for (size_t i = 0; i < N; ++i)
	{
		v.push_back(rand());
		//v.push_back(rand()+i);
		//v.push_back(i);
	}

	size_t begin1 = clock();
	for (auto e : v)
	{
		s.insert(e);
	}
	size_t end1 = clock();
	cout << "set insert:" << end1 - begin1 << endl;

	size_t begin2 = clock();
	for (auto e : v)
	{
		us.insert(e);
	}
	size_t end2 = clock();
	cout << "unordered_set insert:" << end2 - begin2 << endl;


	size_t begin3 = clock();
	for (auto e : v)
	{
		s.find(e);
	}
	size_t end3 = clock();
	cout << "set find:" << end3 - begin3 << endl;

	size_t begin4 = clock();
	for (auto e : v)
	{
		us.find(e);
	}
	size_t end4 = clock();
	cout << "unordered_set find:" << end4 - begin4 << endl << endl;

	cout << s.size() << endl;
	cout << us.size() << endl << endl;;

	size_t begin5 = clock();
	for (auto e : v)
	{
		s.erase(e);
	}
	size_t end5 = clock();
	cout << "set erase:" << end5 - begin5 << endl;

	size_t begin6 = clock();
	for (auto e : v)
	{
		us.erase(e);
	}
	size_t end6 = clock();
	cout << "unordered_set erase:" << end6 - begin6 << endl << endl;
}

 

Comencemos con 10000 números aleatorios como ejemplo:

 Podemos ver que todas las funciones de la serie hash son más rápidas que las ordinarias. Comparemos 100.000 números aleatorios:

 Se puede ver que la serie desordenada es aún más rápida, y otros 1.000.000:

 A partir de los resultados anteriores, podemos ver por qué c ++ agrega una nueva serie hash.Los siguientes son los datos medidos por otra máquina:

 Resumen: en términos de varios escenarios, el rendimiento general de la serie desordenada es mejor, especialmente encontrar es el mejor.

En segundo lugar, el uso de unordered_set

Del mismo modo, veamos si hay alguna diferencia funcional entre set y hash set:

 Como dijimos antes, set es un iterador bidireccional y hash series es un iterador unidireccional. Excepto por la diferencia del iterador, otras funciones son casi iguales. Demostremos cómo usar las funciones básicas:

void unordered_set_test()
{
	unordered_set<int> ust;
	ust.insert(1);
	ust.insert(7);
	ust.insert(4);
	ust.insert(9);
	ust.insert(3);
	for (auto& e : ust)
	{
		cout << e << " ";
	}
	cout << endl;
	unordered_set<int>::iterator it = ust.begin();
	while (it != ust.end())
	{
		cout << *it << " ";
		++it;
	}
}
int main()
{
	unordered_set_test();
}

Ejercicios: 

 La misma diferencia con el conjunto no está ordenado. Lo anterior es el uso de todas las series hash de contenido. A continuación, usamos la serie hash para practicar una pregunta:

Intersección I de dos números:

Ritko Enlace: Rikko

 La intersección solo necesita encontrar los mismos elementos de las dos matrices, y el título nos dice que cada elemento es único, por lo que no hay elementos duplicados.

class Solution {
public:
    vector<int> intersection(vector<int>& nums1, vector<int>& nums2) {
       unordered_set<int> us1(nums1.begin(),nums1.end());
       unordered_set<int> us2(nums2.begin(),nums2.end());
       vector<int> v;
       for (auto& e:us1)
       {
           if (us2.find(e)!=us2.end())
           {
                v.push_back(e);
           }
       }
       return v;
    }
};

Primero colocamos los números de las dos matrices en el conjunto hash respectivamente, luego recorremos el primer conjunto hash (también puede atravesar el segundo) y luego dejamos que el segundo conjunto hash encuentre el primer valor If en un conjunto hash se encuentra, significa que el número es una intersección y lo coloca en la matriz, y finalmente devuelve la matriz.


Resumir

El contenedor de la serie hash es casi el mismo que el contenedor anterior. Es más fácil usar el contenedor STL con frecuencia. En el próximo artículo, explicaremos el principio subyacente de la tabla hash y lo implementaremos. Por lo tanto, si desea Realice la capa subyacente, aún necesita saber cuál es el contenedor. Cómo usarlo, puede realizar la función.

Supongo que te gusta

Origin blog.csdn.net/Sxy_wspsby/article/details/130786386
Recomendado
Clasificación