set/multiset/unordered_set/unordered_multiset关联式容器的基本概念总结
set
set<typename> obj;
set基于RB-Tree实现,RB-Tree具有自动排序的功能,插入元素时,它会自动调整二叉树的排列,把元素放到适当的位置,以保证每个子树根节点键值大于左子树所有节点的键值,小于右子树所有节点的键值;另外,还得保证根节点左子树的高度与右子树高度相等。
multiset
multiset<typename> obj;
multiset和set的唯一区别就是multiset容器允许有重复的元素,插入或删除元素的时间复杂度是O(logn)
unordered_set
unordered_set<typename> obj;
unordered_set基于哈希表实现,是一种无序集合,所以不像基于红黑树的set那样具有排序功能。所以在用迭代器的使用上,set的效率高于unordered_set。
unordered_multiset
unordered_multiset<typename> obj;
基本与unordered_set特点相当,但是允许存在相同的值
set/multiset的特点
- 每个元素都是唯一的,也是元素本身,multiset则可以不唯一
- 元素的值不能被随意改变
- 插入删除搜索效率高,但不如unordered_set,但是占空间小些
- 元素在插入时就已经按照一定的方式排序好了
unordered_set/unordered_multiset的特点
- unordered_set每个key值都是唯一的,unordered_multiset允许有相同的key值。
- 元素的值不能被随意改变
- 元素是无序的
- 插入删除搜索效率高,但是占空间大些
set/multiset的常用方法
begin() 返回指向容器的第一个元素的迭代器
end() 返回指向容器的最后一个元素的下一个地址迭代器
clear() 删除容器里的所有元素
empty() 判断容器是否为空
max_size() 返回元素可能包含元素最大个数
size() 返回当前容器中元素的个数
rbegin() 相当于end()
rend() 相当于begin()
count() 返回某个值元素的个数(由于set不可以存在重复元素,所以相当于判断是否存在这个元素
)
find() 返回一个指向被查找到的元素的迭代器
equal_range() 返回集合中与给定值相等的上下限的两个迭代器
erase() 删除集合中的指定元素
insert() 在集合中插入元素
lower_bound() 返回指向大于某值的第一个元素的迭代器
unordered_set/unordered_multiset的常用方法
begin() 返回指向容器的第一个元素的迭代器
end() 返回指向容器的最后一个元素的下一个地址迭代器
cbegin() 相当于end()
cend() 相当于begin()
clear() 删除容器里的所有元素
empty() 判断容器是否为空
max_size() 返回最大存储量
size() 返回当前容器中元素的个数
count() 返回某个值元素的个数(由于set不可以存在重复元素,所以相当于判断是否存在这个元素)
find() 返回一个指向被查找到的元素的迭代器
equal_range() 返回集合中与给定值相等的上下限的两个迭代器
erase() 删除集合中的指定元素
insert()|emplace() 在集合中插入元素
emplace_hint(迭代器,val) 在指定地方插入元素
lower_bound() 返回指向大于某值的第一个元素的迭代器
swap() 交换元素
bucket_count() 哈希表的返回篮子个数
bucket_size(具体篮子) 返回那个篮子的槽大小
示例代码
//set容器的方法具体示例程序,multiset大致是相同的,举一反三吧
int main(int argc, char** argv) {
set<int> box;
set<int> box2;
set<int>::iterator box_start = box.begin();//指向第一个元素的地址,box.rend()指向第一个元素的前一个地址
set<int>::iterator box_end = box.end(); //指向最后一个元素的下一个地址,box.rbegin()指向最后一个元素
for (int i = 0; i < 10; i++) {
box.emplace(i); //插入一个元素 0,1,2,3,4,5,6,7,8,9,
}
set<int>::iterator it1 = box.emplace_hint(box_start,10); //在特定地方插入一个元素(set是基于红黑树自动排序的,所以只是插入一个元素) 0,1,2,3,4,5,6,7,8,9,10
/***enplace_hint对比其他插入方法有些不同的就是返回值类型是一个迭代器,插入成功返回该迭代器,插入失败返回与插入值相同的已经存在的值的迭代器地址。***/
box.insert(11); //也是插入一个元素 0,1,2,3,4,5,6,7,8,9,10,11
pair<set<int>::iterator,bool> it2 = box.insert(9); //因为set的特性不能存在相同元素,所以插入失败 0,1,2,3,4,5,6,7,8,9,10,11
/***insert,emplace返回值都是pair对象,包括一个迭代器和布尔变量。插入成功返回迭代器地址和true,插入失败返回.end()和false。***/
box.swap(box2); //box的元素和box2的元素交换,因为box2是空的,所以box空了,box2得到了box的所有元素
box.empty(); //返回true
box2.swap(box);
box.empty(); //返回false
auto a = box.count(11); //返回1则存在11这个元素
box.erase(11); //删除11这个元素 0,1,2,3,4,5,6,7,8,9,10
auto b = box.count(11); //返回0这个元素不存在
pair<set<int>::iterator, set<int>::iterator> it3 = box.equal_range(3); //it3->first = 3 ,it3->second = 4
auto it4 = box.find(5); //查找这个元素,返回值是这个元素的迭代器,找不到就返回end()
int maxsize = box.max_size(); //maxsize = 214748364 返回容器最大能存储多少个元素
int size = box.size(); //size = 11 返回当前容器有多少个元素
auto it5 = box.lower_bound(5); //返回第一个大于等于val的迭代器,这里就是返回5的迭代器咯
box.clear(); //清空整个容器
return 0;
}
总结
set和unordered_set每个节点都只能存放一个元素,元素都不能相同。但是由于底层实现原理不同。所以查找、插入、删除,用unordered_set。需要迭代遍历就用set。multiset和unordered_multiset则是可以允许元素相同的,可以按照这4种容器各自的特点灵活使用解决问题。