c++ set用法详解
set
set就是集合,STL的set用二叉树实现,集合中的每个元素只出现一次(参照数学中集合的互斥性),并且是排好序的(默认按键值升序排列)
访问元素的时间复杂度是
1. set 是按照一定次序存储元素的容器
2. 在 set 中,元素的 value 也标识它 (value 就是 key ,类型为 T) ,并且每个 value 必须是唯一的。
set 中的元素不能在容器中修改 ( 元素总是 const) ,但是可以从容器中插入或删除它们。
3. 在内部, set 中的元素总是按照其内部比较对象 ( 类型比较 ) 所指示的特定严格弱排序准则进行
排序。
4. set 容器通过 key 访问单个元素的速度通常比 unordered_set 容器慢,但它们允许根据顺序对
子集进行直接迭代。
5. set 在底层是用二叉搜索树 ( 红黑树 ) 实现的。
注意:
1. 与 map/multimap 不同, map/multimap 中存储的是真正的键值对 , set 中只放
value ,但在底层实际存放的是由 构成的键值对。
2. set 中插入元素时,只需要插入 value 即可,不需要构造键值对。
3. set 中的元素不可以重复 ( 因此可以使用 set 进行去重 ) 。
4. 使用 set 的迭代器遍历 set 中的元素,可以得到有序序列
5. set 中的元素默认按照小于来比较
6. set 中查找某个元素,时间复杂度为: log_2 n
7. set 中的元素不允许修改 ( 为什么 ?)
8. set 中的底层使用二叉搜索树 ( 红黑树 ) 来实现
在c++中,set的头文件是#include
set具有迭代器set::iterator i
定义一个迭代器,名为i 可以把迭代器理解为C语言的指针
set常用操作
set q; //以int型为例 默认按键值升序
set> p; //降序排列
int x;
q.insert(x); //将x插入q中
q.erase(x); //删除q中的x元素,返回0或1,0表示set中不存在x
q.clear(); //清空q
q.empty(); //判断q是否为空,若是返回1,否则返回0
q.size(); //返回q中元素的个数
q.find(x); //在q中查找x,返回x的迭代器,若x不存在,则返回指向q尾部的迭代器即 q.end()
q.lower_bound(x); //返回一个迭代器,指向第一个键值不小于x的元素
q.upper_bound(x); //返回一个迭代器,指向第一个键值大于x的元素
q.rend(); //返回第一个元素的的前一个元素迭代器
q.begin(); //返回指向q中第一个元素的迭代器
q.end(); //返回指向q最后一个元素下一个位置的迭代器
q.rbegin(); //返回最后一个元素
set单元素应用
#include
#include
using namespace std;
void map_test1()
{
set q; //默认按升序排列
q.insert(5);
q.insert(5);
q.insert(5);
cout << "q.size " << q.size() << endl; //输出 1 ,在set插入中相同元素只会存在一个
q.clear(); //清空set
cout << "q.size " << q.size() << "\n\n";
q.insert(4);
q.insert(4);
q.insert(3);
q.insert(3);
q.insert(2);
q.insert(1);
cout << "lower_bound " << *q.lower_bound(3) << endl; //返回3
cout << "upper_bound " << *q.upper_bound(3) << "\n\n"; //返回4
set::iterator i;
for (i = q.begin(); i != q.end(); i++) //set的遍历
cout << *i << " "; //输出1 2 3 4,可见自动按键值排序
cout << endl;
q.erase(4); //删除q中的 4
for (i = q.begin(); i != q.end(); i++) //再次遍历set 只输出 1 2 3
cout << *i << " ";
cout << "\n\n";
set> p; //降序排列
p.insert(1);
p.insert(2);
p.insert(3);
p.insert(4);
p.insert(5);
for (i = p.begin(); i != p.end(); i++)
cout << *i << " ";
cout << endl;
}
int main()
{
map_test1();
return 0;
}
输出结果:
set多元素应用(结构体)
#include
#include
using namespace std;
struct node {
int a, b;
bool operator< (const node W)const
{
return a > W.a; //按a的值升序
}
}t;
void map_test2()
{
set q;
t.a = 1;
t.b = 2;
q.insert(t);
t.a = 4;
t.b = 2;
q.insert(t);
t.a = 3;
t.b = 5;
q.insert(t);
set::iterator i;
for (i = q.begin(); i != q.end(); i++)
{
t = *i;
cout << t.a << " " << t.b << endl;
}
}
int main()
{
map_test2();
return 0;
}
输出结果:
map简介
1. map 是关联容器,它按照特定的次序 ( 按照 key 来比较 ) 存储由键值 key 和值 value 组合而成的元
素。
2. 在 map 中,键值 key 通常用于排序和惟一地标识元素,而值 value 中存储与此键值 key 关联的
内容。键值 key 和值 value 的类型可能不同,并且在 map 的内部, key 与 value 通过成员类型
value_type 绑定在一起,为其取别名称为 pair:
typedef pair value_type;
3. 在内部, map 中的元素总是按照键值 key 进行比较排序的。
4. map 中通过键值访问单个元素的速度通常比 unordered_map 容器慢,但 map 允许根据顺序
对元素进行直接迭代 ( 即对 map 中的元素进行迭代时,可以得到一个有序的序列 ) 。
5. map 支持下标访问符,即在 [] 中放入 key ,就可以找到与 key 对应的 value 。
6. map 通常被实现为二叉搜索树 ( 更准确的说:平衡二叉搜索树 ( 红黑树 )) 。
map的功能
自动建立key - value的对应。key 和 value可以是任意你需要的类型,包括自定义类型。
使用map
使用map得包含map类所在的头文件
#include
map对象是模板类,需要关键字和存储对象两个模板参数:
std:map personnel;
这样就定义了一个用int作为索引,并拥有相关联的指向string的指针.
为了使用方便,可以对模板类进行一下类型定义,
typedef map UDT_MAP_INT_CSTRING;
UDT_MAP_INT_CSTRING enumMap;
map的构造函数
map共提供了6个构造函数,这块涉及到内存分配器这些东西,略过不表,在下面我们将接触到一些map的构造方法,这里要说下的就是,我们通常用如下方法构造一个map:
map的基本操作函数:
begin() 返回指向map头部的迭代器
clear() 删除所有元素
count() 返回指定元素出现的次数, (帮助评论区理解: 因为key值不会重复,所以只能是 1 or 0)
empty() 如果map为空则返回true
end() 返回指向map末尾的迭代器
equal_range() 返回特殊条目的迭代器对
erase() 删除一个元素
find() 查找一个元素
get_allocator() 返回map的配置器
insert() 插入元素
key_comp() 返回比较元素key的函数
lower_bound() 返回键值>=给定元素的第一个位置
max_size() 返回可以容纳的最大元素个数
rbegin() 返回一个指向map尾部的逆向迭代器
rend() 返回一个指向map头部的逆向迭代器
size() 返回map中元素的个数
swap() 交换两个map
upper_bound() 返回键值>给定元素的第一个位置
value_comp() 返回比较元素value的函数
例如:
#include
#include
【总结】
1. map 中的的元素是键值对
2. map 中的 key 是唯一的,并且不能修改
3. 默认按照小于的方式对 key 进行比较
4. map 中的元素如果用迭代器去遍历,可以得到一个有序的序列
5. map 的底层为平衡搜索树 ( 红黑树 ) ,查找效率比较高 O(log_2 N)
6. 支持 [] 操作符, operator[] 中实际进行插入查找。
multiset
multiset的介绍
1. multiset 是按照特定顺序存储元素的容器,其中元素是可以重复的。
2. 在 multiset 中,元素的 value 也会识别它 ( 因为 multiset 中本身存储的就是 组成
的键值对,因此 value 本身就是 key , key 就是 value ,类型为 T). multiset 元素的值不能在容器
中进行修改 ( 因为元素总是 const 的 ) ,但可以从容器中插入或删除。
3. 在内部, multiset 中的元素总是按照其内部比较规则 ( 类型比较 ) 所指示的特定严格弱排序准则
进行排序。
4. multiset 容器通过 key 访问单个元素的速度通常比 unordered_multiset 容器慢,但当使用迭
代器遍历时会得到一个有序序列。
5. multiset 底层结构为二叉搜索树 ( 红黑树 ) 。
注意:
1. multiset 中再底层中存储的是 的键值对
2. mtltiset 的插入接口中只需要插入即可
3. 与 set 的区别是, multiset 中的元素可以重复, set 是中 value 是唯一的
4. 使用迭代器对 multiset 中的元素进行遍历,可以得到有序的序列
5. multiset 中的元素不能修改
6. 在 multiset 中找某个元素,时间复杂度为 $O(log_2 N)$
7. multiset 的作用:可以对元素进行排序
multiset的使用
#include
void TestSet()
{
int array[] = { 2, 1, 3, 9, 6, 0, 5, 8, 4, 7 };
// 注意:multiset在底层实际存储的是的键值对
multiset s(array, array + sizeof(array)/sizeof(array[0]));
for (auto& e : s)
cout << e << " ";
cout << endl;
return 0;
}
multimap
multimap的介绍
1. Multimaps 是关联式容器,它按照特定的顺序,存储由 key 和 value 映射成的键值对
value> ,其中多个键值对之间的 key 是可以重复的。
2. 在 multimap 中,通常按照 key 排序和惟一地标识元素,而映射的 value 存储与 key 关联的内
容。 key 和 value 的类型可能不同,通过 multimap 内部的成员类型 value_type 组合在一起,
value_type 是组合 key 和 value 的键值对 :
typedef pair value_type ;
3. 在内部, multimap 中的元素总是通过其内部比较对象,按照指定的特定严格弱排序标准对
key 进行排序的。
4. multimap 通过 key 访问单个元素的速度通常比 unordered_multimap 容器慢,但是使用迭代
器直接遍历 multimap 中的元素可以得到关于 key 有序的序列。
5. multimap 在底层用二叉搜索树 ( 红黑树 ) 来实现。
注意: multimap 和 map 的唯一不同就是: map 中的 key 是唯一的,而 multimap 中 key 是可以
重复的 。
multimap的使用
multimap 中的接口可以参考 map ,功能都是类似的。
注意:
1. multimap 中的 key 是可以重复的。
2. multimap 中的元素默认将 key 按照小于来比较
3. multimap 中没有重载 operator[] 操作 ( 同学们可思考下为什么 ?) 。
4. 使用时与 map 包含的头文件相同:
底层结构
前面对 map/multimap/set/multiset 进行了简单的介绍,在其文档介绍中发现,这几个容器有个
共同点是: 其底层都是按照二叉搜索树来实现的 ,但是二叉搜索树有其自身的缺陷,假如往树中
插入的元素有序或者接近有序,二叉搜索树就会退化成单支树,时间复杂度会退化成 O(N) ,因此
map 、 set 等关联式容器的底层结构是对二叉树进行了平衡处理,即采用平衡树来实现。