Unordered_set basado en la implementación de la tabla hash del contenedor C++

1. Conceptos básicos

unordered_set es un conjunto de datos desordenados, es decir, los datos no se almacenan en un orden particular. Este es un contenedor de datos basado en una tabla hash. Una tabla hash es esencialmente una matriz A diferencia de las matrices comunes, los valores almacenados en una tabla hash son pares clave-valor. Un par clave-valor significa que se puede obtener un valor correspondiente de acuerdo con un valor clave. En cuanto al valor de la clave, la explicación de Baidu Encyclopedia es que " el valor de la clave (clave) es un concepto en el registro de Windows. El valor de la clave se encuentra al final de la cadena de la estructura del registro, similar a los archivos en el sistema de archivos, incluyendo la computadora actual y los programas de aplicación utilizados al ejecutar La información y los datos de configuración reales. El valor clave contiene varios tipos de datos para cumplir con los requisitos de uso de diferentes entornos. " Obtenga un valor correspondiente a través de un valor clave, que es algo similar a la asignación en matemáticas avanzadas. Un ejemplo simple está aquí para ilustrar el concepto.

Suposición : hay un diccionario chino que contiene todos los caracteres chinos, pero estos caracteres chinos se escriben aleatoriamente en cualquier orden, por lo que si desea encontrar un determinado carácter chino, debe verificar uno por uno desde el principio hasta el final. Si no tiene suerte, este carácter chino está justo al final del diccionario, por lo que debe recorrer todo el diccionario para encontrar el carácter chino que desea buscar.

Optimización : debido a que existe una relación definida entre los caracteres chinos y el pinyin, para mejorar la velocidad de búsqueda, todos los caracteres chinos ahora se ordenan según el pinyin (clave) (el pinyin se puede ordenar según la primera letra y la segunda letra) , y cada Cada pinyin tiene un número de página correspondiente (índice), a partir de esta página, se almacenan los caracteres chinos (valor) correspondientes al pinyin. Entonces, si encuentra el pinyin, también puede encontrar los caracteres chinos correspondientes en el número de página correspondiente. Entre ellos, existe una cierta relación de mapeo fijo entre pinyin y el número de página, que se puede calcular de cierta manera (función hash).
Del ejemplo anterior, se puede ver que este contenedor tiene ventajas considerables en la búsqueda de datos y el recorrido del contenedor, por lo que se puede considerar usar este contenedor para problemas de búsqueda . Además, los datos almacenados en este contenedor son únicos y esta función se puede utilizar para comprobar rápidamente si hay valores duplicados en una determinada secuencia de datos .

2. Uso

  1. Definición e inicialización
// constructing unordered_sets
#include <iostream>
#include <string>
#include <unordered_set>

template<class T>
T cmerge (T a, T b) {
    
     T t(a); t.insert(b.begin(),b.end()); return t; }

int main ()
{
    
    
  std::unordered_set<std::string> first;                                // empty
  std::unordered_set<std::string> second ( {
    
    "red","green","blue"} );    // init list
  std::unordered_set<std::string> third ( {
    
    "orange","pink","yellow"} ); // init list
  std::unordered_set<std::string> fourth ( second );                    // copy
  std::unordered_set<std::string> fifth ( cmerge(third,fourth) );       // move
  std::unordered_set<std::string> sixth ( fifth.begin(), fifth.end() ); // range

  std::cout << "sixth contains:";
  for (const std::string& x: sixth) std::cout << " " << x;
  std::cout << std::endl;

  return 0;

producción:

sixth contains: pink yellow red green orange blue
  1. Método miembro
    (1) begin(): Devuelve un iterador que apunta al primer elemento (¡ iterador (iterable) es una súper interfaz! Es un objeto que puede atravesar una colección, proporciona una interfaz de operación común para varios contenedores y aísla el acceso al contenedor La operación transversal y la implementación subyacente están así desacopladas ).
    prototipo:
<1> container iterator (1)	
      iterator begin() noexcept;
      const_iterator begin() const noexcept;
<2> bucket iterator (2)	
      local_iterator begin ( size_type n );
      const_local_iterator begin ( size_type n ) const;

De acuerdo con el prototipo de función, podemos saber que begin puede devolver dos tipos de iteradores, entre los cuales iterator puede cambiar el valor del elemento apuntado, y const_iterator no se puede cambiar, solo se puede cambiar para que apunte a otros elementos. Es decir, const_iterator puede modificar su propio apuntamiento, pero no puede modificar el valor de la posición apuntada.
(2) end(): Devuelve un iterador que apunta al último elemento.
prototipo:

<1> container iterator (1)	
      iterator end() noexcept;
      const_iterator end() const noexcept;
<2> bucket iterator (2)	
      local_iterator end (size_type n);
      const_local_iterator end (size_type n) const;

Ejemplos de uso de begin y end :

// unordered_set::begin/end example
#include <iostream>
#include <string>
#include <unordered_set>

int main ()
{
    
    
  std::unordered_set<std::string> myset =
  {
    
    "Mercury","Venus","Earth","Mars","Jupiter","Saturn","Uranus","Neptune"};

  std::cout << "myset contains:";
  for ( auto it = myset.begin(); it != myset.end(); ++it )
    std::cout << " " << *it;
  std::cout << std::endl;

  std::cout << "myset's buckets contain:\n";
  for ( unsigned i = 0; i < myset.bucket_count(); ++i) {
    
    
    std::cout << "bucket #" << i << " contains:";
    for ( auto local_it = myset.begin(i); local_it!= myset.end(i); ++local_it )
      std::cout << " " << *local_it;
    std::cout << std::endl;
  }

  return 0;
}

salida :

myset contains: Venus Jupiter Neptune Mercury Earth Uranus Saturn Mars
myset's buckets contain:
bucket #0 contains:
bucket #1 contains: Venus
bucket #2 contains: Jupiter
bucket #3 contains: 
bucket #4 contains: Neptune Mercury
bucket #5 contains: 
bucket #6 contains: Earth
bucket #7 contains: Uranus Saturn
bucket #8 contains: Mars
bucket #9 contains: 
bucket #10 contains: 

(3) cubo(const key_type& k): devuelve el número de cubo cuyo valor de elemento es k ( en un conjunto no ordenado, los elementos no se ordenarán en ningún orden, pero los elementos se agrupan en cada ranura por el valor hash del valor del elemento (Bucker, también se puede traducir como "cubo"), por lo que se puede acceder rápidamente a cada elemento correspondiente a través del valor del elemento (el consumo de tiempo promedio es O (1)). Es similar a buscar un carácter chino a través del pinyin del diccionario Prototipo
:

size_type bucket ( const key_type& k ) const;

Ejemplo de uso

// unordered_set::bucket
#include <iostream>
#include <string>
#include <unordered_set>

int main ()
{
    
    
  std::unordered_set<std::string> myset = {
    
    "water","sand","ice","foam"};

  for (const std::string& x: myset) {
    
    
    std::cout << x << " is in bucket #" << myset.bucket(x) << std::endl;
  }

  return 0;
}

salida :

ice is in bucket #0
foam is in bucket #2
sand is in bucket #2
water is in bucket #4

(4) bucket_count(): Devuelve el número de cubos en el contenedor.
Método prototipo:

size_type bucket_count() const noexcept;

Ejemplo de uso

// unordered_set::bucket_count
#include <iostream>
#include <string>
#include <unordered_set>

int main ()
{
    
    
  std::unordered_set<std::string> myset =
  {
    
    "Mercury","Venus","Earth","Mars","Jupiter","Saturn","Uranus","Neptune"};

  unsigned n = myset.bucket_count();

  std::cout << "myset has " << n << " buckets.\n";

  for (unsigned i=0; i<n; ++i) {
    
    
    std::cout << "bucket #" << i << " contains:";
    for (auto it = myset.begin(i); it!=myset.end(i); ++it)
      std::cout << " " << *it;
    std::cout << "\n";
  }

  return 0;
}

producción

myset has 11 buckets.
bucket #0 contains: 
bucket #1 contains: Venus
bucket #2 contains: Jupiter
bucket #3 contains: 
bucket #4 contains: Neptune Mercury
bucket #5 contains: 
bucket #6 contains: Earth
bucket #7 contains: Uranus Saturn
bucket #8 contains: Mars
bucket #9 contains: 
bucket #10 contains: 

(5) cbegin() y cend() tienen las mismas funciones que begin() y cend(), pero los tipos de devolución son diferentes.Tanto cbegin como cend devuelven const_iterator.
Prototipo del método:

container iterator (1)	
          const_iterator cbegin() const noexcept;
bucket iterator (2)	
          const_local_iterator cbegin ( size_type n ) const;
container iterator (1)	
          const_iterator cend() const noexcept;
bucket iterator (2)	
          const_local_iterator cend ( size_type n ) const;

Aquí hay dos tipos de devolución, uno es el tipo de iterador común (const_iterator) y el otro es el tipo const_local_iterator. Como su nombre lo indica, un iterador local es un iterador que devuelve el depósito actual. Un ejemplo de uso es el siguiente:

// unordered_set::cbegin/cend example
#include <iostream>
#include <string>
#include <unordered_set>

int main ()
{
    
    
  std::unordered_set<std::string> myset =
  {
    
    "Mercury","Venus","Earth","Mars","Jupiter","Saturn","Uranus","Neptune"};

  std::cout << "myset contains:";
  for ( auto it = myset.cbegin(); it != myset.cend(); ++it )//这里的it是const_iterator类型
    std::cout << " " << *it;    // cannot modify *it
  std::cout << std::endl;

  std::cout << "myset's buckets contain:\n";
  for ( unsigned i = 0; i < myset.bucket_count(); ++i) {
    
    
    std::cout << "bucket #" << i << " contains:";
    for ( auto local_it = myset.cbegin(i); local_it!= myset.cend(i); ++local_it )//这里的local_it就是const_local_iterator类型
      std::cout << " " << *local_it;
    std::cout << std::endl;
  }

  return 0;
}

producción

myset contains: Venus Jupiter Neptune Mercury Earth Uranus Saturn Mars
myset's buckets contain:
bucket #0 contains:
bucket #1 contains: Venus
bucket #2 contains: Jupiter
bucket #3 contains: 
bucket #4 contains: Neptune Mercury
bucket #5 contains: 
bucket #6 contains: Earth
bucket #7 contains: Uranus Saturn
bucket #8 contains: Mars
bucket #9 contains: 
bucket #10 contains: 

(6) clear() : Borrar los datos en el contenedor. Este método llama al método destructor del contenedor ~unorder_set. Cabe señalar que el uso de clear() no borra la memoria, sino que solo borra los datos almacenados en el contenedor, es decir, después de usar clear(), la cantidad de elementos en el contenedor es 0 y la memoria solicitada no es liberado Entonces, ¿cómo liberar esta memoria? El bloguero BOOM Zhao Chaochao resumió tres métodos para que los contenedores liberen memoria, que se publican directamente aquí:
1. Método 1: declarar directamente el mismo tipo de contenedor anónimo para intercambiarlo con el contenedor original, y el contenedor anónimo se destruirá automáticamente;

vector( ).swap(num);

2. Método 2: Declare primero un objeto temporal y luego intercambie datos con el contenedor de destino:

vector temp; 
(temp).swap(num); 

El objeto temporal no se ha inicializado, su tamaño de búfer es 0 y no hay datos. Si se intercambian datos con el objeto de destino, el búfer en el contenedor num desaparecerá;

3. Método 3: primero borre la memoria del contenedor de destino y luego use la función de intercambio para intercambiar con el contenedor original, a saber:

num.clear( ); vector(num).swap(num);

Prototipo del método:

void clear() noexcept;

noexcept, tiene dos tipos de efectos: especificador noexcept y operador noexcept. El especificador es para especificar si la función genera una excepción, y el operador debe realizar una verificación en tiempo de compilación y devolver verdadero si se declara que la expresión no generará ninguna excepción.
Ejemplo de uso

// clearing unordered_set
#include <iostream>
#include <string>
#include <unordered_set>

int main ()
{
    
    
  std::unordered_set<std::string> myset =
    {
    
     "chair", "table", "lamp", "sofa" };

  std::cout << "myset contains:";
  for (const std::string& x: myset) std::cout << " " << x;
  std::cout << std::endl;

  myset.clear();
  myset.insert("bed");
  myset.insert("wardrobe");
  myset.insert("nightstand");

  std::cout << "myset contains:";
  for (const std::string& x: myset) std::cout << " " << x;
  std::cout << std::endl;

  return 0;
}

producción

myset contains: sofa lamp table chair
myset contains: nightstand wardrobe bed

(7) count(const key_type& k): cuenta el número de elementos cuyo valor es k en el contenedor. Dado que los elementos almacenados en unordered_set son únicos, este método solo devolverá 0 o 1.
Prototipo del método:

size_type count ( const key_type& k ) const;

Ejemplo de uso

// unordered_set::count
#include <iostream>
#include <string>
#include <unordered_set>

int main ()
{
    
    
  std::unordered_set<std::string> myset = {
    
     "hat", "umbrella", "suit" };

  for (auto& x: {
    
    "hat","sunglasses","suit","t-shirt"}) {
    
    
    if (myset.count(x)>0)
      std::cout << "myset has " << x << std::endl;
    else
      std::cout << "myset has no " << x << std::endl;
  }

  return 0;
}

producción

myset has hat
myset has no sunglasses
myset has suit
myset has no t-shirt

(8) emplace(Args&&... args): cuando no hay un elemento args en el contenedor, inserte los argumentos de datos en el contenedor y devuelva el iterador del elemento y una variable True. Devuelve un iterador al elemento y una variable False si el elemento ya existe en el contenedor.
Prototipo del método:

template <class... Args>
pair <iterator,bool> emplace ( Args&&... args );

Ejemplo de uso

// unordered_set::emplace
#include <iostream>
#include <string>
#include <unordered_set>

int main ()
{
    
    
  std::unordered_set<std::string> myset;

  myset.emplace ("potatoes");
  myset.emplace ("milk");
  myset.emplace ("flour");

  std::cout << "myset contains:";
  for (const std::string& x: myset) std::cout << " " << x;

  std::cout << std::endl;
  return 0;
}

producción

myset contains: potatoes flour milk

Supongo que te gusta

Origin blog.csdn.net/yyl80/article/details/123860099
Recomendado
Clasificación