unordered_set c++使用迭代器构造
时间: 2023-12-10 22:03:04 浏览: 141
你可以使用迭代器来构造一个无序集合(unordered_set)的对象。以下是一个使用迭代器构造无序集合的示例代码:
```cpp
#include <iostream>
#include <unordered_set>
int main() {
std::unordered_set<int> mySet;
// 使用迭代器构造无序集合
std::unordered_set<int>::iterator it;
int arr[] = {1, 2, 3, 4, 5};
for (int i = 0; i < 5; i++) {
mySet.insert(arr[i]);
}
// 打印无序集合中的元素
for (it = mySet.begin(); it != mySet.end(); ++it) {
std::cout << *it << " ";
}
return 0;
}
```
在上面的代码中,我们首先创建了一个空的无序集合 `mySet`。然后,我们使用迭代器 `it` 来遍历数组 `arr`,并将数组中的元素插入到无序集合中。最后,我们使用迭代器遍历无序集合,将元素打印出来。
输出结果将是:1 2 3 4 5,表示无序集合中包含了数组中的这些元素。
相关问题
为什么 set 是双向迭代器,而unordered_ set 是单向迭代器
<think>好的,我现在需要回答用户关于C++中set和unordered_set迭代器方向差异的问题。用户想知道为什么set使用双向迭代器而unordered_set使用单向迭代器,以及标准定义的原因。首先,我需要回忆一下这两种容器的底层实现。
根据用户提供的引用,特别是引用[2]提到,set底层是红黑树,而红黑树是一种二叉搜索树,支持中序遍历,因此迭代器可以双向移动。而unordered_set底层是哈希桶,哈希表的结构导致元素存储不连续,所以迭代器只能单向遍历。这可能是原因之一。
接下来,我需要确认标准库中的定义。引用[3]和[4]提到unordered_set的模板参数包括哈希函数和相等谓词,这进一步说明其基于哈希表的结构。哈希表中的每个桶可能是一个链表或类似结构,遍历时需要逐个访问每个桶中的元素,这可能导致单向迭代器更高效,因为双向移动需要额外的指针,增加存储和处理的开销。
另外,红黑树作为平衡二叉搜索树,每个节点都有左右子节点和父节点的指针,这使得前后移动(++和--操作)相对容易实现。而哈希表的结构不同,元素分布在不同桶中,没有自然的顺序,反向遍历需要维护更多的状态信息,可能效率较低,所以标准库可能选择只支持单向迭代器。
此外,标准库的设计考虑到了性能与功能的平衡。set需要有序性,支持双向迭代器方便用户进行前后遍历,而unordered_set更注重插入和查找的效率,牺牲了顺序性,因此单向迭代器足以满足大多数使用场景,同时减少了实现的复杂性。
总结下来,底层数据结构的差异是主要原因:红黑树支持高效的双向遍历,而哈希表的结构更适合单向遍历。标准库根据这两种结构的特性定义了不同的迭代器类型,以平衡功能和性能。</think>C++中`set`使用双向迭代器、`unordered_set`使用单向迭代器的核心原因在于两者的底层数据结构和设计目标差异。具体分析如下:
---
### 一、底层数据结构差异
1. **`set`基于红黑树实现**
- 红黑树是**平衡二叉搜索树**,每个节点包含父节点指针、左右子节点指针。这种结构天然支持**中序遍历**,且遍历方向可逆。
- 双向迭代器的`++`和`--`操作通过树的中序遍历实现:`++`对应后继节点查找,`--`对应前驱节点查找,时间复杂度为$O(1)$均摊[^2]。
2. **`unordered_set`基于哈希桶实现**
- 哈希表将元素分散存储在不同**桶(bucket)**中,每个桶是单向链表或动态数组。元素的位置由哈希函数决定,**物理存储无连续顺序**。
- 遍历时需要依次访问每个非空桶中的元素,且无法直接反向定位前一个有效桶,因此只能支持单向迭代器[^4]。
---
### 二、功能需求差异
| 特性 | `set` | `unordered_set` |
|---------------------|--------------------------|-------------------------|
| **元素顺序** | 严格按键升序排列 | 无序(哈希依赖) |
| **迭代方向需求** | 需要反向遍历(如`rbegin`)| 仅需单向顺序访问 |
| **典型操作复杂度** | 插入/查找$O(\log n)$ | 插入/查找$O(1)$平均 |
- **`set`的有序性**要求支持双向移动迭代器,例如通过`--`操作反向遍历有序序列。
- **`unordered_set`的无序性**使得反向遍历无实际意义,单向迭代器简化了实现并减少存储开销(无需维护反向指针)。
---
### 三、标准库的明确定义
- **C++标准规定**:
- `set`的迭代器类型为`BidirectionalIterator`(双向迭代器)
- `unordered_set`的迭代器类型为`ForwardIterator`(单向迭代器)
这一设计直接反映了两者底层结构的特性[^3]。
---
### 四、性能权衡示例
```cpp
// set支持反向迭代
std::set<int> s = {1,3,2};
for (auto rit = s.rbegin(); rit != s.rend(); ++rit) {
std::cout << *rit; // 输出 3 2 1
}
// unordered_set反向迭代会编译失败
std::unordered_set<int> us = {1,3,2};
// auto rit = us.rbegin(); // 错误:无rbegin()成员
```
---
unordered_set c++使用
### C++中 `unordered_set` 的用法
`unordered_set` 是 C++ STL 提供的一种哈希表实现的关联容器,它允许存储唯一的关键字并提供快速查找功能。与 `set` 不同的是,`unordered_set` 并不对元素进行排序[^3]。
#### 基本特性
- **唯一性**:`unordered_set` 中的每个元素都是唯一的,不允许重复。
- **无序性**:不像 `set` 使用红黑树来保持有序结构,`unordered_set` 利用哈希函数管理数据分布,因此其内部元素是没有固定顺序的。
- **高效查询**:由于基于哈希表设计,平均情况下可以达到 O(1) 时间复杂度的操作性能(如插入、删除和查找)。
以下是关于如何使用 `unordered_set` 的详细介绍:
---
#### 创建与初始化
可以通过多种方式创建和初始化一个 `unordered_set` 对象:
```cpp
#include <iostream>
#include <unordered_set>
using namespace std;
int main(){
// 初始化方法一:通过列表初始化
unordered_set<string> uset1 = {"apple", "banana", "cherry"};
// 初始化方法二:逐个插入元素
unordered_set<int> uset2;
uset2.insert(1);
uset2.insert(2);
uset2.insert(3);
return 0;
}
```
上述代码展示了两种常见的初始化手段——直接赋值以及动态插入[^1]。
---
#### 插入操作
向 `unordered_set` 添加新项非常直观:
```cpp
uset1.insert("grape"); // 单独插入一项
uset1.emplace("orange"); // emplace 和 insert 功能相似,在某些场景下效率更高
```
注意,如果尝试插入已存在的键,则该操作会被忽略而不引发错误。
---
#### 删除操作
移除指定元素也很简便:
```cpp
if (uset1.erase("banana") > 0){
cout << "'banana' was successfully removed." << endl;
} else {
cout << "'banana' does not exist in the set." << endl;
}
```
这里需要注意返回值的意义;成功删除时返回受影响的数量,否则为零。
---
#### 查找操作
检查某个特定值是否存在或者获取迭代器指向目标位置均可轻松完成:
```cpp
auto it = uset1.find("apple");
if(it != uset1.end()){
cout << *it << " found!" << endl;
}else{
cout << "Not Found!" << endl;
}
// 或者更简洁的方式判断存在与否
bool exists = (uset1.count("pear") > 0);
cout << boolalpha << exists << endl;
```
其中 `.find()` 返回对应元素的位置指针,`.count()` 给定关键字计数结果要么是0要么是1因为集合里不会有重复条目。
---
#### 迭代遍历
最后我们来看一下怎样访问整个容器内的所有成员:
```cpp
for(const auto& elem : uset1){
cout << elem << ' ';
}
cout << '\n';
```
此循环语句利用范围基表达式逐一打印出每一个项目名称。
---
#### 性能考量及其他注意事项
尽管大多数时候 `unordered_set` 能够带来接近常量时间级别的存取速度优势,但在极端条件下比如糟糕散列冲突率较高情形下也可能退化至线性扫描级别O(n)[^4]。所以实际应用过程中应当合理选取合适的hash function以优化整体表现效果。
另外值得注意的一点就是虽然两者都属于关联型容器范畴之内但是它们之间存在着本质上的差异即是否有内在次序关系这一点决定了各自适用场合的不同之处。
---
阅读全文
相关推荐















