【unordered_set】

C++ 中的 unordered_set:一种高效的无序集合容器
在 C++ 标准库中,unordered_set 是一个高度优化的关联式容器,用于存储具有唯一性的元素。与 std::set 不同,unordered_set 不维护元素的有序性,而是通过哈希表(Hash Table)来组织内部数据。这种底层实现方式使得 unordered_set 在元素的插入、删除和查找操作上通常能达到平均常数时间复杂度 O ( 1 ) O(1)O(1),这使其在需要快速访问和去重的场景下表现卓越。

unordered_set 的核心特性
无序性(Unordered):元素在容器中的存储顺序取决于其哈希值以及哈希表的内部管理策略,而非元素的自然顺序或插入顺序。
唯一性(Uniqueness):unordered_set 只存储唯一值。尝试插入已存在的元素将被忽略。
基于哈希表(Hash Table Based):容器内部使用哈希表实现。每个元素通过一个哈希函数映射到一个存储位置(桶),从而实现快速访问。
平均常数时间复杂度(Average Constant Time Complexity):在理想的哈希函数和负载因子下,insert、erase 和 find 等操作的平均时间复杂度为 O ( 1 ) O(1)O(1)。在最坏情况下(如极端哈希冲突),这些操作可能退化到 O ( n ) O(n)O(n),其中 n nn 是容器中元素的数量。
何时选用 unordered_set?
unordered_set 是以下应用场景的理想选择:

需要对大量元素进行快速的存在性检查(查找)。
需要高效地去重。
对元素的存储顺序没有要求。
相较于基于红黑树实现的 std::set(其操作时间复杂度为 O ( l o g n ) O(\log n)O(
logn)),unordered_set 在平均情况下的性能优势显著,尤其适用于对时间效率要求极高的场景。

示例:unordered_set 的基本用法
以下示例展示了如何创建、插入、查找和删除 unordered_set 中的元素:

#include
#include
#include

int main() {
// 创建一个存储字符串的 unordered_set
std::unordered_setstd::string citySet;

// 插入元素
citySet.insert("Beijing");
citySet.insert("Shanghai");
citySet.insert("Guangzhou");
citySet.insert("Shenzhen");
citySet.insert("Shanghai"); // 尝试插入重复元素,将被忽略

// 输出集合中的元素
std::cout << "Cities in the set (unordered): ";
for (const auto& city : citySet) {
    std::cout << city << " ";
}
std::cout << std::endl;

// 查找元素
std::string searchCity = "Guangzhou";
if (citySet.find(searchCity) != citySet.end()) {
    std::cout << searchCity << " is in the set." << std::endl;
} else {
    std::cout << searchCity << " is not in the set." << std::endl;
}

std::string anotherCity = "Chengdu";
 if (citySet.find(anotherCity) != citySet.end()) {
    std::cout << anotherCity << " is in the set." << std::endl;
} else {
    std::cout << anotherCity << " is not in the set." << std::endl;
}


// 删除元素
std::string eraseCity = "Beijing";
size_t num_removed = citySet.erase(eraseCity); // 返回被删除元素的数量 (0 或 1)
if (num_removed > 0) {
    std::cout << eraseCity << " was removed from the set." << std::endl;
} else {
    std::cout << eraseCity << " was not found in the set." << std::endl;
}


// 输出删除后的集合元素
std::cout << "Cities after removal: ";
for (const auto& city : citySet) {
    std::cout << city << " ";
}
std::cout << std::endl;

return 0;

}

代码解析:
头文件:需要包含 头文件。
创建容器:通过 std::unordered_set containerName; 创建一个 unordered_set 实例。
插入元素:使用 insert() 成员函数添加元素。
查找元素:find() 成员函数返回一个迭代器。如果找到元素,迭代器指向该元素;否则,返回 end() 迭代器。
删除元素:erase() 成员函数可以接受元素值或迭代器作为参数来删除元素。
遍历:可以使用范围 for 循环或迭代器进行遍历,但请注意遍历顺序是不确定的。
运行结果:
Cities in the set (unordered): Shenzhen Shanghai Guangzhou Beijing
Guangzhou is in the set.
Chengdu is not in the set.
Beijing was removed from the set.
Cities after removal: Shenzhen Shanghai Guangzhou

注意:实际输出的元素顺序可能与上述不同,因为 unordered_set 不保证顺序。

例题
给定一个未排序的整数数组 nums ,找出数字连续的最长序列(不要求序列元素在原数组中连续)的长度。

请你设计并实现时间复杂度为 O(n) 的算法解决此问题。

代码截图:
【unordered_set】_第1张图片

unordered_set 的优势与考虑因素
优势:
高性能:在平均情况下,提供 O ( 1 ) O(1)O(1) 的插入、删除和查找性能,远优于基于比较的容器(如 std::set 或 std::map)在处理大量数据时的性能。
灵活:适用于任何可哈希的数据类型作为元素类型。
考虑因素(潜在的局限性):
无序性:如果应用场景对元素的顺序有要求,unordered_set 则不适用,应考虑 std::set 或其他有序容器。
内存开销:哈希表通常需要额外的空间来管理桶和处理冲突,可能比基于树的容器消耗更多内存。
哈希函数和冲突:哈希表的性能高度依赖于所使用的哈希函数和哈希冲突的处理策略。一个不良的哈希函数可能导致大量冲突,使平均时间复杂度退化,接近 O ( n ) O(n)O(n)。对于自定义类型作为元素,需要提供一个合适的哈希函数。
最坏情况性能:尽管平均性能出色,但在极少数情况下(如恶意构造的输入导致频繁哈希冲突),操作的时间复杂度可能高达 O ( n ) O(n)O(n)。
总结
unordered_set 是 C++ 中一个强大且高效的容器,尤其适用于需要快速查找和去重,并且不关心元素顺序的应用。其底层基于哈希表的实现保证了大多数操作在平均意义上的常数时间复杂度。然而,开发者在使用时也应考虑其无序性、潜在的内存开销以及对哈希函数的要求,并根据具体的应用需求权衡选择。理解其工作原理有助于更有效地利用这一容器,解决实际编程问题。

你可能感兴趣的:(哈希算法,算法)