前言:
map和set有很多相似的操作,比如其函数部分几乎是一样的,map介绍中还会与本文的一些部分进行比较,文章的排版也和set部分几乎一样,大家可以比较着学,自行食用~ —>C++ STL map合集(包括map、multimap、unordered_map、unordered_multimap)
1.set介绍
C++ STL中标准关联容器set, multiset, 包括map, multimap内部采用的就是一种非常高效的平衡检索二叉树:红黑树,也称为RB树(Red-Black Tree)。RB树的统计性能要好于一般平衡二叉树,其插入搜索速度都是log级别的,总之效率很高。
set 的含义是集合,它是一个有序的容器,里面的元素都是排序好的,支持插入,删除,查找等操作,就像一个集合一样。所有的操作的都是严格在logn时间之内完成,效率非常高。set 和 multiset 的区别是:set 插入的元素不能相同,但是 multiset 可以相同。
但是unordered_set是用哈希实现的,(其实带unordered都是哈希,un order ed 就是英语无序),它的底层原理是哈希,因此插入是随机的,复杂度O(1)。而删除操作与内部哈希函数有关系,但多数情况是常数级别。
说明:
unordered_set、unordered_multiset头文件:
#include<unordered_set>
set, multiset头文件:
#include <set>
2.map、set关联式容器总结:
**set和map在实现上有很多相似的地方,具体可以看C++ STL map合集(包括map、multimap、unordered_map、unordered_multimap)
C++STL包含了序列式容器和关联式容器:
序列式容器里面存储的是元素本身,其底层为线性序列的数据结构。比如:vector,list,deque等。
关联式容器里面存储的是<key, value>结构的键值对,在数据检索时比序列式容器效率更高,因为是用树和哈希实现的,因此也不能直接用下标来访问元素。 比如:set、map、unordered_set、unordered_map等。
- 注意: C++STL当中的stack、queue和priority_queue属于容器适配器,它们默认使用的基础容器分别是deque、deque和vector。
关联式容器 | 容器结构 | 底层实现 |
---|---|---|
set、map、multiset、multimap | 树型结构 | 平衡搜索树(红黑树) |
unordered_set、unordered_map、unordered_multiset、unordered_multimap | 哈希结构 | 哈希表 |
一、set
1.定义:
set<Type> s
其实有很多种,只列举一个常用的。
注意:
- 每个元素的键值都唯一,不允许两个元素有相同的键值。
- 所有元素都会根据元素的键值自动排序(默认从小到大)。
- 如果要从大到小排序,需要加入greater< type>: 例如:
set<int,greater<int> >s;
- set 中元素的值不能直接被改变。
2.基本函数:
这些都是要知道的,其实很多是通用的比如:begin\end\clear\size\empty…
s.begin() //返回指向第一个元素的迭代器
s.end() //返回指向最后一个元素的迭代器
s.clear() //清除所有元素
s.count() //返回某个值元素的个数
s.empty() //如果集合为空,返回true,否则返回false
swap() //交换两个集合容器的值
s.erase() //删除集合中的元素
s.find(k) //返回一个指向被查找到元素的迭代器
s.insert() //在集合中插入元素
s.lower_bound(k) //返回一个迭代器,指向键值大于等于k的第一个元素
s.upper_bound(k) //返回一个迭代器,指向键值大于k的第一个元素
s.size() //集合中元素的数目
3.set的遍历
set需要用迭代器遍历或者使用范围基础的 for 循环(C++11 及以上)
#include <iostream>
#include <set>
using namespace std;
int main()
{
set<int> s;
s.insert(2);
s.insert(4);
s.insert(6);
s.insert(10); // set 会自动排序
s.insert(8);
set<int>::iterator it; // 使用迭代器
for (it = s.begin(); it != s.end(); ++it) {
cout << *it << " ";
}
cout << endl;
// 或者使用范围基础的 for 循环(C++11 及以上)
for (const auto& element : s) {
cout << element << " ";
}
cout << endl;
return 0;
}
4.insert函数
s.insert(1);//插入数
s.insert(a[i]);//插入数组
5.erase函数
- set中的删除操作是不进行任何的错误检查的,比如定位器的是否合法等等,所以用的时候自己一定要注意。
1.erase(iterator) :删除定位器iterator指向的值
2.erase(first,second):删除定位器first和second之间的值
//注意这里的区间是左闭右开的。
//而且要注意是迭代器的值而不是简单的数来表示位置。
3.erase(key_value):删除键值key_value的值
可以自己去编译器里试试这三种情况:
#include <iostream>
#include <set>
using namespace std;
int main(){
set<int> s;
set<int>::const_iterator it;
set<int>::iterator first;
set<int>::iterator second;
for(int i=1; i<=10; ++i)
s.insert(i);
//第一种删除,删掉1
s.erase(s.begin());
//第二种删除,删掉2和3
first = s.begin();
second = s.begin();
second++;
second++;
s.erase(first,second);
//第三种删除,删掉8
s.erase(8);
cout<<"删除后 set 中元素是 :";
for(it=s.begin(); it!=s.end(); ++it)
cout<<*it<<" ";
cout<<endl;
return 0;
}
6.find函数用法:
返回一个指向被查找到元素的迭代器,如果没找到则返回end()
- 我们只能用来判断set种是否有这个值,却无法知道其具体的位置在哪,没找到返回end()。
//返回一个指向被查找到元素的迭代器,如果没找到则返回end()
#include <iostream>
#include <set>
using namespace std;
int main(){
set<int> s;
s.insert(1);
s.insert(2);
s.insert(3);
s.insert(4);
set<int>::iterator it;
if((it=s.find(4))!=s.end())
cout<<*it<<endl;
return 0;
}
7.lower_bound()、upper_bound() 的用法
lower_bound(key_value) :返回第一个大于等于key_value的定位器
upper_bound(key_value):返回第一个大于key_value的定位器
- 一样的我们只能用来判断set大于/等于的这个值是什么,却无法知道其具体的位置在哪。 超过了最大值会返回end();
#include <iostream>
#include <set>
using namespace std;
int main(){
set<int> s;
s.insert(1);
s.insert(3);
s.insert(4);
s.insert(6);
cout<<*s.lower_bound(1)<<endl;
cout<<*s.lower_bound(2)<<endl;
cout<<*s.upper_bound(3)<<endl;
return 0;
}
输出就是1 2 3
set还可以自定义排序,感觉几乎用不着,想了解自己查吧~
二、multiset
multiset容器与set容器的底层实现一样,都是平衡搜索树(红黑树),其次,multiset容器和set容器所提供的成员函数的接口都是基本一致的,这里就不再列举了,multiset容器和set容器的唯一区别就是,multiset允许键值冗余,即multiset容器当中存储的元素是可以重复的。
因此find函数有所不同:
find(val) //返回第一个值为val的元素的迭代器
count在这里才有应用,在普通set里只有0,1,find函数就能代替。
#include <iostream>
#include <set>
using namespace std;
int main()
{
multiset<int> ms;
//插入元素(允许重复)
ms.insert(1);
ms.insert(4);
ms.insert(3);
ms.insert(3);
ms.insert(2);
ms.insert(2);
ms.insert(3);
for (auto e : ms)
{
cout << e << " ";
}
cout << endl; //1 2 2 3 3 3 4
return 0;
}
三、unordered_set
无序集合(unordered_set)是一种使用哈希表实现的无序关联容器,其中键被哈希到哈希表的索引位置,因此插入操作总是随机的。无序集合上的所有操作在平均情况下都具有常数时间复杂度O(1),但在最坏情况下,时间复杂度可以达到线性时间O(n),这取决于内部使用的哈希函数,但实际上它们表现非常出色,通常提供常数时间的查找操作。
函数还是上面的函数,就是少了begin、end等因为是无序的
四、unordered_multiset
大多数操作与 unordered_set 的操作方式相似,但有一些不同之处:
- 例如,如果某个值v在unordered_multiset中出现了t次,当调用 erase 时,v将被完全删除
但是,因为 find 函数返回找到值的第一个位置的迭代器,我们可以将此迭代器传递给erase而不是传递实际值,从而仅删除一个副本