An article to familiarize you with unordered_set and its mock implementation

Please add image description

Definition of unordered_set

unordered_setis a container in the C++ standard library that stores unique elements that are not organized in any particular order. It is implemented based on a hash table, so it can provide constant-time insertion, deletion and lookup operations in the average case.

The following is a basic example using the unordered_set definition:

#include <iostream>
#include <unordered_set>

int main() {
    
    
    // 创建一个unordered_set
    std::unordered_set<int> mySet;

    // 向unordered_set中插入元素
    mySet.insert(5);
    mySet.insert(2);
    mySet.insert(8);

    // 查找元素
    if (mySet.find(2) != mySet.end()) {
    
    
        std::cout << "元素 2 存在于unordered_set中" << std::endl;
    }

    // 遍历unordered_set中的元素
    for (const int& value : mySet) {
    
    
        std::cout << value << " ";
    }
    std::cout << std::endl;

    return 0;
}

unordered_set provides an efficient way to store and retrieve distinct values ​​without preserving their order. Therefore, if you need to store elements in order, consider using std::set or another ordered container.

1. Template definition of unordered_set

template < class Key,                        // unordered_set::key_type/value_type
           class Hash = hash<Key>,           // unordered_set::hasher
           class Pred = equal_to<Key>,       // unordered_set::key_equal
           class Alloc = allocator<Key>      // unordered_set::allocator_type
           > class unordered_set;
  1. Key (key/value type): This is the type of element stored in unordered_set. For example, if you want to create a unordered_set that stores integers, set Key to int.
  2. Hash (hash function type): This is the function object type used to calculate the hash value of the element. By default, this is std::hash<Key>, which provides hash functions for most built-in types. You can also provide a custom hash function.
  3. Pred (key comparison function type): This is the function object type used to compare elements. By default, this is std::equal_to<Key>, which performs the standard equals comparison. You can provide a custom comparison function to determine whether elements are equal based on your needs.
  4. Alloc (allocator type): This is the allocator type used to allocate and free memory. By default, it is std::allocator<Key>, used for standard memory management. You usually only need to customize the allocator in special cases.

2. Member types of unordered_set

The following aliases are member types of unordered_set. They are widely used as parameter and return types by member functions
Insert image description here

unordered_set constructor

empty (1)	
explicit unordered_set ( size_type n = /* see below */,
                         const hasher& hf = hasher(),
                         const key_equal& eql = key_equal(),
                         const allocator_type& alloc = allocator_type() );
explicit unordered_set ( const allocator_type& alloc );

range (2)	
template <class InputIterator>
         unordered_set ( InputIterator first, InputIterator last,
                         size_type n = /* see below */,
                         const hasher& hf = hasher(),
                         const key_equal& eql = key_equal(),
                         const allocator_type& alloc = allocator_type() );

copy (3)	
unordered_set ( const unordered_set& ust );
unordered_set ( const unordered_set& ust, const allocator_type& alloc );

move (4)	
unordered_set ( unordered_set&& ust );
unordered_set ( unordered_set&& ust, const allocator_type& alloc );

initializer list (5)	
unordered_set ( initializer_list<value_type> il,
                size_type n = /* see below */,
                const hasher& hf = hasher(),
                const key_equal& eql = key_equal(),
                const allocator_type& alloc = allocator_type() );
  1. explicit unordered_set(size_type n = /* see below */, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& alloc = allocator_type());
    • Creates an empty unordered_set.
    • n: The initial number of buckets in the hash table.
    • hf: Hash function.
    • eql: Key comparison function.
    • alloc: allocator.
  2. template <class InputIterator> unordered_set(InputIterator first, InputIterator last, size_type n = /* see below */, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& alloc = allocator_type());
    • Creates from elements in iterator range [first, last). unordered_set
    • n: The initial number of buckets in the hash table.
    • hf: Hash function.
    • eql: Key comparison function.
    • alloc: allocator.
  3. unordered_set(const unordered_set& ust);
    • Creates a new from a copy of another unordered_set. unordered_set
  4. unordered_set(unordered_set&& ust);
    • moves the constructor, creating a new with the contents of another unordered_set and the source is no longer Contains these elements. unordered_setunordered_set
  5. unordered_set(initializer_list<value_type> il, size_type n = /* see below */, const hasher& hf = hasher(), const key_equal& eql = key_equal(), const allocator_type& alloc = allocator_type());
    • Create using initialization list il. unordered_set
    • n: The initial number of buckets in the hash table.
    • hf: Hash function.
    • eql: Key comparison function.
    • alloc: allocator.

Here are some examples of creating and initializing std::unordered_set using different constructors:

#include <iostream>
#include <unordered_set>

int main() {
    
    
    // 示例 1: 使用默认构造函数创建一个空的 unordered_set
    std::unordered_set<int> mySet1;

    // 示例 2: 使用迭代器范围初始化 unordered_set
    std::unordered_set<int> mySet2({
    
    1, 2, 3, 4, 5}); // 初始化列表
    std::unordered_set<int> mySet3(mySet2.begin(), mySet2.end()); // 从另一个集合复制

    // 示例 3: 使用复制构造函数创建一个新的 unordered_set
    std::unordered_set<int> mySet4(mySet3);

    // 示例 4: 使用移动构造函数创建一个新的 unordered_set
    std::unordered_set<int> mySet5(std::move(mySet4));

    // 示例 5: 使用初始化列表和自定义哈希函数
    std::unordered_set<std::string> mySet6({
    
    "apple", "banana", "cherry"}, 10, std::hash<std::string>(), std::equal_to<std::string>());

    // 遍历并打印 unordered_set 中的元素
    for (const int& value : mySet3) {
    
    
        std::cout << value << " ";
    }
    std::cout << std::endl;

    for (const std::string& fruit : mySet6) {
    
    
        std::cout << fruit << " ";
    }
    std::cout << std::endl;

    return 0;
}

This example demonstrates how to use different constructors to create and initialize std::unordered_set, including using the default constructor, iterator range, copy constructor, move constructor, and initialization list. It also demonstrates how to customize hash functions and comparison functions.

unordered_set assignment operator overloading

copy (1)	
unordered_set& operator= ( const unordered_set& ust );
move (2)	
unordered_set& operator= ( unordered_set&& ust );
initializer list (3)	
unordered_set& operator= ( intitializer_list<value_type> il );

Here are the different assignment operator overloads for std::unordered_set:

  1. unordered_set& operator= ( const unordered_set& ust );
    • Copy assignment operator. Replaces the current content with the content of another unordered_set. unordered_set
  2. unordered_set& operator= ( unordered_set&& ust );
    • move assignment operator. The content of the current is replaced with the content of another unordered_set, and the source no longer contains these elements. unordered_setunordered_set
  3. unordered_set& operator= ( initializer_list<value_type> il );
    • initialization list assignment operator. Replaces the current contents of unordered_set with elements from the initialization list.

These assignment operators allow you to assign elements from one std::unordered_set to another in different ways, including copying, moving, and using an initializer list. This helps manage and update unordered_set's content.

Here are examples of assignment operator overloading using different types:

#include <iostream>
#include <unordered_set>

int main() {
    
    
    // 示例 1: 复制赋值运算符
    std::unordered_set<int> set1 = {
    
    1, 2, 3};
    std::unordered_set<int> set2;
    set2 = set1; // 复制 set1 到 set2

    std::cout << "set2 after copy assignment: ";
    for (const int& value : set2) {
    
    
        std::cout << value << " ";
    }
    std::cout << std::endl;

    // 示例 2: 移动赋值运算符
    std::unordered_set<std::string> set3 = {
    
    "apple", "banana", "cherry"};
    std::unordered_set<std::string> set4;
    set4 = std::move(set3); // 移动 set3 到 set4

    std::cout << "set3 after move assignment: ";
    for (const std::string& fruit : set3) {
    
    
        std::cout << fruit << " "; // set3 不再包含元素
    }
    std::cout << std::endl;

    std::cout << "set4 after move assignment: ";
    for (const std::string& fruit : set4) {
    
    
        std::cout << fruit << " "; // set4 包含被移动的元素
    }
    std::cout << std::endl;

    // 示例 3: 初始化列表赋值运算符
    std::unordered_set<char> set5;
    set5 = {
    
    'a', 'b', 'c'}; // 使用初始化列表赋值

    std::cout << "set5 after initializer list assignment: ";
    for (const char& letter : set5) {
    
    
        std::cout << letter << " ";
    }
    std::cout << std::endl;

    return 0;
}

This example demonstrates the use of different types of assignment operator overloading, including copying, moving, and assignment using an initializer list. Note that the source unordered_set no longer contains elements after the move assignment.

unordered_set capacity function (Capacity)

  1. bool empty() const noexcept;
    • This function returns a Boolean value indicating whether the collection is empty. If the collection is empty, it returns true; otherwise, it returns false.
    • const noexceptIndicates that this is a constant member function that does not throw an exception.
  2. size_type size() const noexcept;
    • This function returns the number of elements in the collection, which is the size of the collection.
    • const noexceptIndicates that this is a constant member function that does not throw an exception.
    • The type returned by size_type is an unsigned integer type, usually std::size_t.
  3. size_type max_size() const noexcept;
    • This function returns the maximum number of elements the collection may contain.
    • const noexceptIndicates that this is a constant member function that does not throw an exception.
    • The type returned by size_type is an unsigned integer type, usually std::size_t.

These functions allow you to query the state of a collection when using std::unordered_set , such as checking whether it is empty, getting the size of the collection, and knowing the maximum number of elements a collection may contain. Since they are const member functions and do not throw exceptions, they can be called safely.

The following are member functions using std::unordered_set empty(), size(), and max_size() Example:

#include <iostream>
#include <unordered_set>

int main() {
    
    
    std::unordered_set<int> mySet;

    // 检查集合是否为空
    if (mySet.empty()) {
    
    
        std::cout << "集合为空" << std::endl;
    } else {
    
    
        std::cout << "集合不为空" << std::endl;
    }

    // 添加元素到集合
    mySet.insert(1);
    mySet.insert(2);
    mySet.insert(3);

    // 获取集合的大小
    std::cout << "集合的大小为: " << mySet.size() << std::endl;

    // 获取集合可能包含的最大元素数量
    std::cout << "集合可能包含的最大元素数量为: " << mySet.max_size() << std::endl;

    return 0;
}

This example demonstrates how to use the empty() function to check if a collection is empty, the size() function to get the size of a collection, and the max_size() function to get the maximum number of elements a set may contain. According to the operation in the example, the program will output the corresponding information.

unordered_set iterators (Iterators)

1. begin()

container iterator (1)	
      iterator begin() noexcept;
	  const_iterator begin() const noexcept;
bucket iterator (2)	
      local_iterator begin ( size_type n );
	  const_local_iterator begin ( size_type n ) const;
  1. iterator begin() noexcept;
    • The non-const version of the begin() function returns an iterator pointing to the first element in the collection. You can use this iterator to iterate over the elements of a collection.
    • const noexceptIndicates that this is a non-const member function that does not throw an exception.
  2. const_iterator begin() const noexcept;
    • The constant version of the begin() function returns a constant iterator pointing to the first element in the collection. This constant iterator only allows reading elements, not modifying them.
    • const noexceptIndicates that this is a constant member function that does not throw an exception.
  3. local_iterator begin(size_type n);
    • This function is used to get the iterator pointing to the first local element in the bucket n. In a hash table, elements are distributed into different buckets, local_iterator allows you to iterate over elements in a specific bucket.
    • size_type nThe parameter specifies the index of the bucket.
  4. const_local_iterator begin(size_type n) const;
    • This is the constant version of the begin(size_type n) function, returning a constant iterator pointing to the first local element in the bucket n.

2. end()

container iterator (1)	
      iterator end() noexcept;
	  const_iterator end() const noexcept;
bucket iterator (2)	
      local_iterator end (size_type n);
	  const_local_iterator end (size_type n) const;
  1. iterator end() noexcept;
    • The non-const version of the end() function returns an iterator pointing to the end of the collection. It is usually used in conjunction with begin() to traverse the elements of the entire collection.
    • const noexceptIndicates that this is a non-const member function that does not throw an exception.
  2. const_iterator end() const noexcept;
    • The constant version of the end() function returns a constant iterator pointing to the end of the collection. This constant iterator only allows reading elements, not modifying them.
    • const noexceptIndicates that this is a constant member function that does not throw an exception.
  3. local_iterator end(size_type n);
    • This function is used to get the iterator pointing to the end local element of the bucket n. In a hash table, elements are distributed into different buckets, local_iterator allows you to iterate over elements in a specific bucket.
    • size_type nThe parameter specifies the index of the bucket.
  4. const_local_iterator end(size_type n) const;
    • This is the constant version of the end(size_type n) function, returning a constant iterator pointing to the end local element of the bucket n.

3. cbegin()

container iterator (1)	
const_iterator cbegin() const noexcept;
bucket iterator (2)	
const_local_iterator cbegin ( size_type n ) const;
  1. const_iterator cbegin() const noexcept;
    • This function returns a constant iterator pointing to the first element in the collection. Similar to begin(), but this is a const member function that only allows the elements to be read but not modified.
    • const noexceptIndicates that this is a constant member function that does not throw an exception.
  2. const_local_iterator cbegin(size_type n) const;
    • This function is used to obtain a constant iterator pointing to the first local element in the bucket n. In a hash table, elements are distributed into different buckets, const_local_iterator allows you to iterate over elements in a specific bucket.
    • size_type nThe parameter specifies the index of the bucket

4. cend()

container iterator (1)	
const_iterator cend() const noexcept;
bucket iterator (2)	
const_local_iterator cend ( size_type n ) const;
  1. const_iterator cend() const noexcept;
    • This function returns a constant iterator pointing to the end of the collection. Similar to end(), but this is a const member function that only allows the elements to be read but not modified.
    • const noexceptIndicates that this is a constant member function that does not throw an exception.
  2. const_local_iterator cend(size_type n) const;
    • This function is used to obtain an iterator pointing to the constant local end element of the bucket n. In a hash table, elements are distributed into different buckets, const_local_iterator allows you to iterate over elements in a specific bucket.
    • size_type nThe parameter specifies the index of the bucket.

unordered_set element search function

1. find

iterator find ( const key_type& k );
const_iterator find ( const key_type& k ) const;

std::unordered_set provides thefind() member function that finds whether the specified key value exists in the collection and returns an iterator pointing to the element containing the key value (if present) or to end() (if not present).

The following are two overloaded forms of the function in std::unordered_set:find()

  1. iterator find(const key_type& k);
    • This version of thefind() function returns an iterator pointing to the element containing the key valuek if it exists, otherwise it returns a pointer to Iterator for end().
    • You can use this iterator if you want to make modifications to the found elements.
  2. const_iterator find(const key_type& k) const;
    • This is the constant version of thefind() function, returning a constant iterator for finding the element containing the key valuek if it exists, otherwise Returns a constant iterator pointing to end().
    • If you just need to find an element without modifying it, you can use this constant iterator.

Thesefind() functions are a common way to find a specific key value instd::unordered_set. They allow you to quickly determine whether a key exists in the collection. and gives you access to the element if it exists.

The following is an example of using the function of std::unordered_set:find()

#include <iostream>
#include <unordered_set>

int main() {
    
    
    std::unordered_set<int> mySet = {
    
    1, 2, 3, 4, 5};

    // 查找特定键值
    int keyToFind = 3;
    auto it = mySet.find(keyToFind);

    // 检查元素是否存在
    if (it != mySet.end()) {
    
    
        std::cout << "键值 " << keyToFind << " 存在于集合中,对应的元素是 " << *it << std::endl;
    } else {
    
    
        std::cout << "键值 " << keyToFind << " 不存在于集合中" << std::endl;
    }

    // 尝试查找一个不存在的键值
    int nonExistentKey = 6;
    auto nonExistentIt = mySet.find(nonExistentKey);

    if (nonExistentIt != mySet.end()) {
    
    
        std::cout << "键值 " << nonExistentKey << " 存在于集合中,对应的元素是 " << *nonExistentIt << std::endl;
    } else {
    
    
        std::cout << "键值 " << nonExistentKey << " 不存在于集合中" << std::endl;
    }

    return 0;
}

In this example, we use the find() function to find specific key values ​​(3 and 6) and output the corresponding information based on the search results. If the key value exists in the collection, find() returns an iterator pointing to the element, otherwise returns an iterator pointing to end().

2. count

size_type count ( const key_type& k ) const;

std::unordered_set provides the count() member function for counting the number of occurrences of a specified key value in the collection. This function accepts a key value as a parameter and returns an integer representing the number of times the key value appears in the collection.

The following is an example usage of the function in std::unordered_set:count()

#include <iostream>
#include <unordered_set>

int main() {
    
    
    std::unordered_set<int> mySet = {
    
    1, 2, 2, 3, 4, 5, 5, 5};

    // 计算指定键值的出现次数
    int keyToCount = 2;
    std::unordered_set<int>::size_type occurrences = mySet.count(keyToCount);

    std::cout << "键值 " << keyToCount << " 在集合中出现了 " << occurrences << " 次" << std::endl;

    // 计算一个不存在的键值的出现次数
    int nonExistentKey = 6;
    std::unordered_set<int>::size_type nonExistentOccurrences = mySet.count(nonExistentKey);

    std::cout << "键值 " << nonExistentKey << " 在集合中出现了 " << nonExistentOccurrences << " 次" << std::endl;

    return 0;
}

In this example, we use the count() function to count the number of occurrences of a specified key value in the collection. For key values ​​that exist (for example, 2), count() will return the actual number of occurrences of the key value in the collection, while for key values ​​that do not exist (for example, 6), count() will return 0. This function is very useful for counting the number of occurrences of an element.

3. equal_range

pair<iterator,iterator>
   equal_range ( const key_type& k );
pair<const_iterator,const_iterator>
   equal_range ( const key_type& k ) const;

std::unordered_set provides the equal_range() member function for finding a range of elements equal to a specified key value. This function returns a std::pair containing two iterators pointing to the start and end of the range.

Here are example uses of two different overloads of the function in std::unordered_set:equal_range()

#include <iostream>
#include <unordered_set>

int main() {
    
    
    std::unordered_set<int> mySet = {
    
    1, 2, 2, 3, 4, 5, 5, 5};

    // 查找与指定键值相等的元素范围
    int keyToFind = 2;
    auto range = mySet.equal_range(keyToFind);

    std::cout << "元素范围,键值 " << keyToFind << ": ";
    for (auto it = range.first; it != range.second; ++it) {
    
    
        std::cout << *it << " ";
    }
    std::cout << std::endl;

    // 尝试查找一个不存在的键值的元素范围
    int nonExistentKey = 6;
    auto nonExistentRange = mySet.equal_range(nonExistentKey);

    std::cout << "元素范围,键值 " << nonExistentKey << ": ";
    for (auto it = nonExistentRange.first; it != nonExistentRange.second; ++it) {
    
    
        std::cout << *it << " ";
    }
    std::cout << std::endl;

    return 0;
}

In this example, we use the equal_range() function to find a range of elements that are equal to the specified key value. For an existing key value (e.g. 2), equal_range() returns a containing range std::pair and uses an iterator to traverse the range. For a non-existent key value (e.g. 6), equal_range() returns a range, but this range is empty, so the iterator ranges are equal to begin() and end(), there will be no element traversal. This function is useful for finding elements with the same key value in a collection.

unordered_set modifier function

1. location

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

emplace() Member function used to efficiently add new elements by constructing them and inserting them into a collection. This function returns a std::pair containing an iterator and a boolean value.

The following is an example usage of the function in std::unordered_set:emplace()

#include <iostream>
#include <unordered_set>
#include <string>

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

    // 使用 emplace() 插入新元素
    auto result1 = mySet.emplace("apple");
    auto result2 = mySet.emplace("banana");

    // 检查插入结果
    if (result1.second) {
    
    
        std::cout << "插入成功: " << *result1.first << std::endl;
    } else {
    
    
        std::cout << "插入失败: " << *result1.first << " 已存在" << std::endl;
    }

    if (result2.second) {
    
    
        std::cout << "插入成功: " << *result2.first << std::endl;
    } else {
    
    
        std::cout << "插入失败: " << *result2.first << " 已存在" << std::endl;
    }

    return 0;
}

In this example, we use the emplace() function to insert a new element into std::unordered_set. This function allows you to create an element by passing the parameters required to construct the element without having to explicitly create the object and then insert it. emplace() returns a std::pair, where the first member is an iterator pointing to the inserted element (or existing element), The second member is a Boolean value indicating whether the insertion was successful. If the element already exists, emplace() returns the iterator and false of the existing element, otherwise returns the iterator and true

2. emplace_hint

template <class... Args>
iterator emplace_hint ( const_iterator position, Args&&... args );

emplace_hint() Member function used to insert a new element at the specified position (hint). This function accepts a constant iterator position as a hint, and arguments for constructing new elements.

The following is an example usage of the function in std::unordered_set:emplace_hint()

#include <iostream>
#include <unordered_set>
#include <string>

int main() {
    
    
    std::unordered_set<std::string> mySet = {
    
    "apple", "banana"};

    // 使用 emplace_hint() 在提示位置插入新元素
    std::string hint = "cherry";
    auto hintIterator = mySet.find(hint); // 获取提示位置的迭代器
    if (hintIterator != mySet.end()) {
    
    
        mySet.emplace_hint(hintIterator, "grape");
    } else {
    
    
        std::cout << "提示位置不存在" << std::endl;
    }

    // 输出集合中的元素
    for (const std::string& fruit : mySet) {
    
    
        std::cout << fruit << " ";
    }
    std::cout << std::endl;

    return 0;
}

In this example, we first obtain an iterator at the hint position, and then use the emplace_hint() function to insert a new element at the hint position. emplace_hint() Accepts an iterator position as the hint position and inserts the new element before this position. If the prompt location does not exist, you can take corresponding processing measures, as shown in the example.

3. insert

(1)	pair<iterator,bool> insert ( const value_type& val );
(2)	pair<iterator,bool> insert ( value_type&& val );
(3)	iterator insert ( const_iterator hint, const value_type& val );
(4)	iterator insert ( const_iterator hint, value_type&& val );
(5)	template <class InputIterator>
    void insert ( InputIterator first, InputIterator last );
(6)	void insert ( initializer_list<value_type> il );

insert() Member function used to insert elements into the collection. Here are the different forms of these insert() functions:

  1. (1) pair<iterator, bool> insert(const value_type& val);
    • This function inserts a value val into the collection.
    • If the element is inserted successfully, it returns an iterator containing the inserted element and true of std::pair .
    • If the element already exists in the collection, it returns an iterator pointing to the existing element and false.
  2. (2) pair<iterator, bool> insert(value_type&& val);
    • This function is used to insert an rvalue reference val into the collection.
    • is similar to (1), if the element is successfully inserted, it returns an iterator containing the inserted element and true.
    • If the element already exists in the collection, it returns an iterator pointing to the existing element and false.
  3. (3) iterator insert(const_iterator hint, const value_type& val);
    • This function inserts a value val into the collection and uses the hint position hint to optimize the insertion operation.
    • Returns an iterator pointing to the inserted element or the existing element if the element already exists.
  4. (4) iterator insert(const_iterator hint, value_type&& val);
    • This is an rvalue reference version of (3), similar to (3), which uses hint position hint to optimize insertion operate.
  5. (5) template <class InputIterator> void insert(InputIterator first, InputIterator last);
    • This function accepts an iterator range [first, last) and inserts the elements in the range into the collection.
  6. (6) void insert(initializer_list<value_type> il);
    • This function takes an initializer list and inserts its elements into the collection.

These different forms ofinsert() functions allow you to insert elements intostd::unordered_set in a variety of ways, including insertion of individual elements, using hint positions Insert, range insert and initializer list insert. When inserting, it will check whether the element already exists. If it exists, the same element will not be inserted repeatedly.

Here are some examples of using different forms of the insert() function to insert elements into std::unordered_set:

#include <iostream>
#include <unordered_set>

int main() {
    
    
    std::unordered_set<int> mySet;

    // (1) 插入单个元素
    auto result1 = mySet.insert(1);
    auto result2 = mySet.insert(2);

    if (result1.second) {
    
    
        std::cout << "成功插入元素 " << *result1.first << std::endl;
    } else {
    
    
        std::cout << "元素 " << *result1.first << " 已存在" << std::endl;
    }

    if (result2.second) {
    
    
        std::cout << "成功插入元素 " << *result2.first << std::endl;
    } else {
    
    
        std::cout << "元素 " << *result2.first << " 已存在" << std::endl;
    }

    // (3) 使用提示位置插入元素
    std::unordered_set<int>::iterator hint = mySet.find(3);
    if (hint != mySet.end()) {
    
    
        mySet.insert(hint, 4);
    } else {
    
    
        std::cout << "提示位置不存在" << std::endl;
    }

    // (5) 范围插入元素
    std::unordered_set<int> anotherSet = {
    
    5, 6, 7};
    mySet.insert(anotherSet.begin(), anotherSet.end());

    // (6) 使用初始化列表插入元素
    mySet.insert({
    
    8, 9, 10});

    // 输出集合中的元素
    for (const int& element : mySet) {
    
    
        std::cout << element << " ";
    }
    std::cout << std::endl;

    return 0;
}

In this example, we use a different form of the insert() function to insert elements into std::unordered_set. The program outputs the results of each insertion and displays the appropriate message if the element already exists. Finally, we iterate through the collection to see the inserted elements. This demonstrates how to operate using different insert() functions. std::unordered_set

4. erase

by position (1)	
iterator erase ( const_iterator position );
by key (2)	
size_type erase ( const key_type& k );
range (3)	
iterator erase ( const_iterator first, const_iterator last );

Here are examples of using different forms of the erase() function to remove elements from std::unordered_set:

#include <iostream>
#include <unordered_set>

int main() {
    
    
    std::unordered_set<int> mySet = {
    
    1, 2, 3, 4, 5};

    // (1) 通过位置删除元素
    std::unordered_set<int>::const_iterator position = mySet.find(3);
    if (position != mySet.end()) {
    
    
        mySet.erase(position);
    }

    // (2) 通过键值删除元素
    int keyToRemove = 5;
    size_t elementsRemoved = mySet.erase(keyToRemove);
    std::cout << "删除了 " << elementsRemoved << " 个键值为 " << keyToRemove << " 的元素" << std::endl;

    // (3) 通过范围删除元素
    std::unordered_set<int>::const_iterator first = mySet.begin();
    std::unordered_set<int>::const_iterator last = mySet.end();
    mySet.erase(first, last);

    // 输出剩余的元素
    for (const int& element : mySet) {
    
    
        std::cout << element << " ";
    }
    std::cout << std::endl;

    return 0;
}

In this example, we use a different form of the erase() function to delete the elements in std::unordered_set. First, we delete elements by position, key value, and range, and then output the collection contents after the deletion operation. This demonstrates how to use the erase() function to remove elements.

5. clear

void clear() noexcept;

clear() member function, used to clear the collection, that is, delete all elements in the collection to make it an empty collection. This function takes no parameters, does not throw an exception, and has the noexcept guarantee, which means it will not throw an exception.

The following is an example of using the clear() function:

#include <iostream>
#include <unordered_set>

int main() {
    
    
    std::unordered_set<int> mySet = {
    
    1, 2, 3, 4, 5};

    // 输出原始集合内容
    std::cout << "原始集合内容:";
    for (const int& element : mySet) {
    
    
        std::cout << " " << element;
    }
    std::cout << std::endl;

    // 清空集合
    mySet.clear();

    // 输出清空后的集合内容
    std::cout << "清空后的集合内容:";
    for (const int& element : mySet) {
    
    
        std::cout << " " << element;
    }
    std::cout << std::endl;

    return 0;
}

In this example, we first output the contents of the original collection and then empty the collection using the clear() function. Finally, we output the contents of the collection again, and we can see that the emptying operation makes the collection an empty collection. This is a quick way to empty a collection.

6. swap

void swap ( unordered_set& ust );

swap()Member functions are used to exchange the contents of two sets, that is, to exchange elements of one set with elements of another set.

The following is an example of using the swap() function:

#include <iostream>
#include <unordered_set>

int main() {
    
    
    std::unordered_set<int> set1 = {
    
    1, 2, 3};
    std::unordered_set<int> set2 = {
    
    4, 5, 6};

    // 输出交换前的集合内容
    std::cout << "交换前 set1:";
    for (const int& element : set1) {
    
    
        std::cout << " " << element;
    }
    std::cout << std::endl;

    std::cout << "交换前 set2:";
    for (const int& element : set2) {
    
    
        std::cout << " " << element;
    }
    std::cout << std::endl;

    // 使用 swap() 函数交换两个集合的内容
    set1.swap(set2);

    // 输出交换后的集合内容
    std::cout << "交换后 set1:";
    for (const int& element : set1) {
    
    
        std::cout << " " << element;
    }
    std::cout << std::endl;

    std::cout << "交换后 set2:";
    for (const int& element : set2) {
    
    
        std::cout << " " << element;
    }
    std::cout << std::endl;

    return 0;
}

In this example, we first output the contents of the two sets before swapping, then swapped their contents using the swap() function, and finally output the contents of the two sets after swapping. . As you can see, the contents of the collection have been swapped. This function is very useful and can be used to efficiently exchange the contents of two collections without copying elements.

unordered_set bucket function

1. bucket_count

size_type bucket_count() const noexcept;

bucket_count() Member function to get the number of buckets in the hash table. The hash table is the underlying data structure used to implement std::unordered_set, which distributes elements into different buckets to improve lookup performance.

The following is an example of using the bucket_count() function:

#include <iostream>
#include <unordered_set>

int main() {
    
    
    std::unordered_set<int> mySet = {
    
    1, 2, 3, 4, 5};

    // 获取桶的数量
    std::unordered_set<int>::size_type bucketCount = mySet.bucket_count();

    std::cout << "哈希表中的桶的数量为: " << bucketCount << std::endl;

    return 0;
}

In this example, we use the bucket_count() function to get the number of buckets in the hash table and print it to the console. This value can help you understand the size and performance of the hash table. Note that the number of buckets can vary as the hash table is tuned to maintain performance.

2. max_bucket_count

size_type max_bucket_count() const noexcept;

max_bucket_count()Member function used to obtain the maximum number of buckets supported by the hash table. This value is usually implementation-limited and depends on the computer's hardware and operating system.

The following is an example of using the max_bucket_count() function:

#include <iostream>
#include <unordered_set>

int main() {
    
    
    std::unordered_set<int> mySet = {
    
    1, 2, 3, 4, 5};

    // 获取支持的最大桶的数量
    std::unordered_set<int>::size_type maxBucketCount = mySet.max_bucket_count();

    std::cout << "哈希表支持的最大桶的数量为: " << maxBucketCount << std::endl;

    return 0;
}

In this example, we use the max_bucket_count() function to get the maximum number of buckets supported by the hash table and output it to the console. This value is defined by the standard library implementation and is usually a large integer representing the maximum number of buckets that the hash table can hold. This helps you understand the capacity limit of the hash table.

3. bucket_size

size_type bucket_size ( size_type n ) const;

bucket_size(size_type n) Member function used to obtain the number of elements in the specified bucket. You need to pass the index of a bucket n and this function will return the number of elements in that bucket.

The following is an example of using the bucket_size() function:

#include <iostream>
#include <unordered_set>

int main() {
    
    
    std::unordered_set<int> mySet = {
    
    1, 2, 3, 4, 5};

    // 获取第一个桶中的元素数量
    std::unordered_set<int>::size_type bucketSize = mySet.bucket_size(0);

    std::cout << "第一个桶中的元素数量为: " << bucketSize << std::endl;

    return 0;
}

In this example, we use the bucket_size() function to get the number of elements in the first bucket and output it to the console. This can help you understand the number of elements in a specific bucket, which is helpful for debugging and understanding the distribution of the hash table. Note that the number of elements in different buckets may vary.

4. bucket

size_type bucket ( const key_type& k ) const;

bucket(const key_type& k) Member function that gets the index of the bucket associated with a specific key value k. This function returns a size_type value, indicating the index of the bucket corresponding to the key value k.

The following is an example of using the bucket() function:

#include <iostream>
#include <unordered_set>

int main() {
    
    
    std::unordered_set<int> mySet = {
    
    1, 2, 3, 4, 5};

    int keyToFind = 3;

    // 获取键值 3 对应的桶的索引
    std::unordered_set<int>::size_type bucketIndex = mySet.bucket(keyToFind);

    std::cout << "键值 " << keyToFind << " 对应的桶的索引为: " << bucketIndex << std::endl;

    return 0;
}

In this example, we use the bucket() function to get the index of the bucket corresponding to key value 3 and output it to the console. This function helps you understand how a specific key value is distributed in the hash table, and in which bucket it is located. Different key values ​​may be in different buckets.

unordered_set hash strategy function

1. load_factor

float load_factor() const noexcept;

load_factor()Member function used to obtain the load factor of the current hash table. The load factor is the ratio of the number of elements to the number of buckets in the hash table. It can be used to measure how full a hash table is, and is often used to determine when a hash table needs to be resized to maintain performance.

load_factor() returns a float value indicating the current load factor.

The following is an example of using the load_factor() function:

#include <iostream>
#include <unordered_set>

int main() {
    
    
    std::unordered_set<int> mySet = {
    
    1, 2, 3, 4, 5};

    // 获取当前哈希表的负载因子
    float currentLoadFactor = mySet.load_factor();

    std::cout << "当前哈希表的负载因子为: " << currentLoadFactor << std::endl;

    return 0;
}

In this example, we use the load_factor() function to get the load factor of the current hash table and output it to the console. Load factor helps you monitor how full your hash table is and adjust the hash table size as needed to maintain performance. Typically, a hash table resize operation is triggered when the load factor is less than a certain threshold.

2. max_load_factor

  1. (1) float max_load_factor() const noexcept;
    • This function is used to obtain the maximum load factor of the current hash table. The maximum load factor is the maximum amount of filling a hash table can tolerate. By default, it is usually a small floating point number (e.g. 0.75).
    • This function returns a float value indicating the maximum load factor of the current hash table.
  2. (2) void max_load_factor(float z);
    • This function is used to set the maximum load factor of the hash table to the specified value z.
    • You can use this function to adjust the maximum load factor of a hash table to control when a hash table resize operation is triggered.

Here are examples of using these two functions:

#include <iostream>
#include <unordered_set>

int main() {
    
    
    std::unordered_set<int> mySet = {
    
    1, 2, 3, 4, 5};

    // 获取当前哈希表的最大负载因子
    float currentMaxLoadFactor = mySet.max_load_factor();
    std::cout << "当前哈希表的最大负载因子为: " << currentMaxLoadFactor << std::endl;

    // 设置新的最大负载因子
    float newMaxLoadFactor = 0.6;
    mySet.max_load_factor(newMaxLoadFactor);

    // 再次获取最大负载因子,检查是否已更新
    float updatedMaxLoadFactor = mySet.max_load_factor();
    std::cout << "更新后的最大负载因子为: " << updatedMaxLoadFactor << std::endl;

    return 0;
}

In this example, we first use the max_load_factor() function to get the current maximum load factor, and then use the max_load_factor() function to set the new maximum load factor. This allows you to adjust how full the hash table is as needed to control when a hash table resize operation is triggered.

3. rehash

rehash(size_type n)Member function that resizes the number of buckets in a hash table to accommodate the specified number of buckets. This can be used to manually adjust the size of the hash table to improve performance or meet specific needs.

The following is an example of using the rehash() function:

#include <iostream>
#include <unordered_set>

int main() {
    
    
    std::unordered_set<int> mySet = {
    
    1, 2, 3, 4, 5};

    // 获取当前哈希表的桶数量
    std::unordered_set<int>::size_type currentBucketCount = mySet.bucket_count();
    std::cout << "当前哈希表的桶数量为: " << currentBucketCount << std::endl;

    // 手动调整哈希表的桶数量
    std::unordered_set<int>::size_type newBucketCount = 10;
    mySet.rehash(newBucketCount);

    // 再次获取桶数量,检查是否已更新
    std::unordered_set<int>::size_type updatedBucketCount = mySet.bucket_count();
    std::cout << "更新后的哈希表的桶数量为: " << updatedBucketCount << std::endl;

    return 0;
}

In this example, we first use the bucket_count() function to get the current number of buckets, and then use the rehash() function to manually adjust the number of buckets in the hash table . This allows you to control the size of the hash table as needed to meet performance or memory needs. Adjusting the number of buckets in a hash table may require redistributing elements and therefore may impact performance

4. reserve

reserve(size_type n) member function that reserves enough bucket space to hold at least n elements to improve performance. The parameters of this function n represent the number of elements reserved, but not the number of buckets.

The following is an example of using the reserve() function:

#include <iostream>
#include <unordered_set>

int main() {
    
    
    std::unordered_set<int> mySet = {
    
    1, 2, 3, 4, 5};

    // 获取当前哈希表的桶数量
    std::unordered_set<int>::size_type currentBucketCount = mySet.bucket_count();
    std::cout << "当前哈希表的桶数量为: " << currentBucketCount << std::endl;

    // 预留足够的桶空间以容纳至少 10 个元素
    std::unordered_set<int>::size_type reserveCount = 10;
    mySet.reserve(reserveCount);

    // 再次获取桶数量,检查是否已更新
    std::unordered_set<int>::size_type updatedBucketCount = mySet.bucket_count();
    std::cout << "更新后的哈希表的桶数量为: " << updatedBucketCount << std::endl;

    return 0;
}

In this example, we first use the bucket_count() function to get the current number of buckets, and then use the reserve() function to reserve enough bucket space to Holds at least 10 elements. This helps improve the performance of insert and lookup operations because the hash table does not need to be resized as frequently. Note that the number of buckets may be greater than the specified number of elements to ensure performance.

Unordered_set opens hash form simulation implementation

The same source code is used in my last unordered_map blog, and the simulation implementation is similar.

All codes of HashTable.h

#pragma once
#include<iostream>
#include<utility>
#include<vector>
#include<string>
using namespace std;
template<class K>
struct HashFunc
{
    
    
	size_t operator()(const K& key)
	{
    
    
		return (size_t)key;
	}
};

template<>
struct HashFunc<string>
{
    
    
	size_t operator()(const string& key)
	{
    
    
		size_t val = 0;
		for (auto ch : key)
		{
    
    
			val *= 131;
			val += ch;
		}

		return val;
	}
};
namespace Bucket
{
    
    
	template<class T>
	struct HashNode
	{
    
    
		T _data;
		HashNode<T>* _next;

		HashNode(const T& data)
			:_data(data)
			, _next(nullptr)
		{
    
    }
	};

	// 前置声明
	template<class K, class T, class Hash, class KeyOfT>
	class HashTable;

	template<class K, class T, class Hash, class KeyOfT>
	struct __HashIterator
	{
    
    
		typedef HashNode<T> Node;
		typedef HashTable<K, T, Hash, KeyOfT> HT;
		typedef __HashIterator<K, T, Hash, KeyOfT> Self;

		Node* _node;
		HT* _pht;

		__HashIterator(Node* node, HT* pht)
			:_node(node)
			, _pht(pht)
		{
    
    }

		T& operator*()
		{
    
    
			return _node->_data;
		}

		T* operator->()
		{
    
    
			return &_node->_data;
		}

		Self& operator++()
		{
    
    
			if (_node->_next)
			{
    
    
				// 当前桶中迭代
				_node = _node->_next;
			}
			else
			{
    
    
				// 找下一个桶
				Hash hash;
				KeyOfT kot;
				size_t i = hash(kot(_node->_data)) % _pht->_tables.size();
				++i;
				for (; i < _pht->_tables.size(); ++i)
				{
    
    
					if (_pht->_tables[i])
					{
    
    
						_node = _pht->_tables[i];
						break;
					}
				}

				// 说明后面没有有数据的桶了
				if (i == _pht->_tables.size())
				{
    
    
					_node = nullptr;
				}
			}

			return *this;
		}

		bool operator!=(const Self& s) const
		{
    
    
			return _node != s._node;
		}

		bool operator==(const Self& s) const
		{
    
    
			return _node == s._node;
		}
	};

	template<class K, class T, class Hash, class KeyOfT>
	class HashTable
	{
    
    
		typedef HashNode<T> Node;

		template<class K, class T, class Hash, class KeyOfT>
		friend struct __HashIterator;
	public:
		typedef __HashIterator<K, T, Hash, KeyOfT> iterator;

		iterator begin()
		{
    
    
			for (size_t i = 0; i < _tables.size(); ++i)
			{
    
    
				if (_tables[i])
				{
    
    
					return iterator(_tables[i], this);
				}
			}

			return end();
		}

		iterator end()
		{
    
    
			return iterator(nullptr, this);
		}

		~HashTable()
		{
    
    
			for (size_t i = 0; i < _tables.size(); ++i)
			{
    
    
				Node* cur = _tables[i];
				while (cur)
				{
    
    
					Node* next = cur->_next;
					delete cur;
					cur = next;
				}
				_tables[i] = nullptr;
			}
		}

		inline size_t __stl_next_prime(size_t n)
		{
    
    
			static const size_t __stl_num_primes = 28;
			static const size_t __stl_prime_list[__stl_num_primes] =
			{
    
    
				53, 97, 193, 389, 769,
				1543, 3079, 6151, 12289, 24593,
				49157, 98317, 196613, 393241, 786433,
				1572869, 3145739, 6291469, 12582917, 25165843,
				50331653, 100663319, 201326611, 402653189, 805306457,
				1610612741, 3221225473, 4294967291
			};

			for (size_t i = 0; i < __stl_num_primes; ++i)
			{
    
    
				if (__stl_prime_list[i] > n)
				{
    
    
					return __stl_prime_list[i];
				}
			}

			return -1;
		}

		pair<iterator, bool> Insert(const T& data)
		{
    
    
			Hash hash;
			KeyOfT kot;

			// 去重
			iterator ret = Find(kot(data));
			if (ret != end())
			{
    
    
				return make_pair(ret, false);
			}

			// 负载因子到1就扩容
			if (_size == _tables.size())
			{
    
    
				vector<Node*> newTables;
				newTables.resize(__stl_next_prime(_tables.size()), nullptr);
				// 旧表中节点移动映射新表
				for (size_t i = 0; i < _tables.size(); ++i)
				{
    
    
					Node* cur = _tables[i];
					while (cur)
					{
    
    
						Node* next = cur->_next;

						size_t hashi = hash(kot(cur->_data)) % newTables.size();
						cur->_next = newTables[hashi];
						newTables[hashi] = cur;

						cur = next;
					}

					_tables[i] = nullptr;
				}

				_tables.swap(newTables);
			}

			size_t hashi = hash(kot(data)) % _tables.size();
			// 头插
			Node* newnode = new Node(data);
			newnode->_next = _tables[hashi];
			_tables[hashi] = newnode;
			++_size;

			return make_pair(iterator(newnode, this), true);
		}

		iterator Find(const K& key)
		{
    
    
			if (_tables.size() == 0)
			{
    
    
				return end();
			}

			Hash hash;
			KeyOfT kot;
			size_t hashi = hash(key) % _tables.size();
			Node* cur = _tables[hashi];
			while (cur)
			{
    
    
				if (kot(cur->_data) == key)
				{
    
    
					return iterator(cur, this);
				}

				cur = cur->_next;
			}

			return end();
		}

		bool Erase(const K& key)
		{
    
    
			if (_tables.size() == 0)
			{
    
    
				return false;
			}

			Hash hash;
			KeyOfT kot;
			size_t hashi = hash(key) % _tables.size();
			Node* prev = nullptr;
			Node* cur = _tables[hashi];
			while (cur)
			{
    
    
				if (kot(cur->_data) == key)
				{
    
    
					// 1、头删
					// 2、中间删
					if (prev == nullptr)
					{
    
    
						_tables[hashi] = cur->_next;
					}
					else
					{
    
    
						prev->_next = cur->_next;
					}

					delete cur;
					--_size;

					return true;
				}

				prev = cur;
				cur = cur->_next;
			}

			return false;
		}

		size_t Size()
		{
    
    
			return _size;
		}

		// 表的长度
		size_t TablesSize()
		{
    
    
			return _tables.size();
		}

		// 桶的个数
		size_t BucketNum()
		{
    
    
			size_t num = 0;
			for (size_t i = 0; i < _tables.size(); ++i)
			{
    
    
				if (_tables[i])
				{
    
    
					++num;
				}
			}

			return num;
		}

		size_t MaxBucketLenth()
		{
    
    
			size_t maxLen = 0;
			for (size_t i = 0; i < _tables.size(); ++i)
			{
    
    
				size_t len = 0;
				Node* cur = _tables[i];
				while (cur)
				{
    
    
					++len;
					cur = cur->_next;
				}

				if (len > maxLen)
				{
    
    
					maxLen = len;
				}
			}

			return maxLen;
		}

	private:
		vector<Node*> _tables;
		size_t _size = 0; // 存储有效数据个数
	};
};

Implement unordered_set using hash table encapsulation

#pragma once

#include "HashTable.h"

namespace yulao
{
    
    
	template<class K, class Hash = HashFunc<K>>
	class unordered_set
	{
    
    
		struct SetKeyOfT
		{
    
    
			const K& operator()(const K& key)
			{
    
    
				return key;
			}
		};
	public:
		typedef typename Bucket::HashTable<K, K, Hash, SetKeyOfT>::iterator iterator;

		iterator begin()
		{
    
    
			return _ht.begin();
		}

		iterator end()
		{
    
    
			return _ht.end();
		}

		pair<iterator, bool> insert(const K& key)
		{
    
    
			return _ht.Insert(key);
		}
	private:
		Bucket::HashTable<K, K, Hash, SetKeyOfT> _ht;
	};
}

Guess you like

Origin blog.csdn.net/kingxzq/article/details/133707310