文章目录
- set
- 1.关联式容器
- 2.键值对
- 3. set
- 3.1 set介绍
- 3.2 set的使用
- 3.2.1 pair
- 3.2.2 find
- 3.2.3 lower_bound
- 3.3 multiset
- 3.3.1 multiset的介绍
- 3.3.2 multiset的使用
- 3.3.3 find
- 3.3.4 equal_range
- 3.3.5 erase
set
1.关联式容器
在初阶阶段,我们已经接触过STL中的部分容器,比如:vector、list、deque、forward_list(C++11)等,这些容器统称为 序列式容器 ,因为其底层为线性序列的数据结构,里面存储的是元素本身。
那什么是关联式容器?它与序列式容器有什么区别?
关联式容器也是用来存储数据的,与序列式容器不同的是,其里面存储的是 <key, value> 结构的键值对,在数据检索时比序列式容器效率更高。
2.键值对
用来表示具有一一对应关系的一种结构,该结构中一般只包含两个成员变量key和value,key代表键值,value表示与key对应的信息。比如:现在要建立一个英汉互译的字典,那该字典中必然有英文单词与其对应的中文含义,而且,英文单词与其中文含义是一一对应的关系,即通过该应该单词,在词典中就可以找到与其对应的中文含义
3. set
3.1 set介绍
set文档介绍
翻译:
-
set是按照一定次序存储元素的容器
-
在set中,元素的value也标识它(value就是key,类型为T),并且每个value必须是唯一的。set中的元素不能在容器中修改(元素总是const),但是可以从容器中插入或删除它们。
-
在内部,set中的元素总是按照其内部比较对象(类型比较)所指示的特定严格弱排序准则进行排序。
-
set容器通过key访问单个元素的速度通常比unordered_set容器慢,但它们允许根据顺序对子集进行直接迭代。
-
set在底层是用二叉搜索树(红黑树)实现的。
注意:
- 与map/multimap不同,map/multimap中存储的是真正的键值对<key, value>,set中只放value,但在底层实际存放的是由<value, value>构成的键值对。
- set中插入元素时,只需要插入value即可,不需要构造键值对。
- set中的元素不可以重复(因此可以使用set进行去重)。
- 使用set的迭代器遍历set中的元素,可以得到有序序列
- set中的元素默认按照小于来比较
- set中查找某个元素,时间复杂度为: l o g 2 n log_2 n log2n
- set中的元素不允许修改(为什么?)
- set中的底层使用二叉搜索树(红黑树)来实现
3.2 set的使用
- set的模板参数列表
T: set中存放元素的类型,实际在底层存储<value, value>的键值对。
Compare:set中元素默认按照小于来比较
Alloc:set中元素空间的管理方式,使用STL提供的空间配置器管理
#include <iostream>
using namespace std;
#include <set>
void test_set()
{
set<int> s;
s.insert(5);
s.insert(2);
s.insert(6);
s.insert(3);
s.insert(1);
s.insert(3);
s.insert(3);
s.insert(3);
s.insert(5);
s.insert(6);
set<int>::iterator it = s.begin();
while(it != s.end())
{
cout << *it << " ";
++it;
}
}
int main()
{
test_set();
return 0;
}
验证结果:
当然,遍历set值,也可以使用范围for:
#include <iostream>
using namespace std;
#include <set>
void test_set()
{
set<int> s;
s.insert(5);
s.insert(2);
s.insert(6);
s.insert(3);
s.insert(1);
s.insert(3);
s.insert(3);
s.insert(3);
s.insert(5);
s.insert(6);
set<int>::iterator it = s.begin();
while(it != s.end())
{
cout << *it << " ";
++it;
}
cout << endl;
for(auto e : s)
{
cout << e << " ";
}
}
int main()
{
test_set();
return 0;
}
运行结果对比:
正所谓只要支持迭代器,就一定支持范围for!!
可以发现,有相同的值只返回了一次,而且走的是中序遍历。
注:set的底层是红黑树,本质也是一个二叉搜索树,当有相同的值时便不会再插入了,返回的是false
这是就一个问题了,查找到相同值时,布尔值是如何返回接受的呢?
答:通过pair接受返回值
3.2.1 pair
这里来查看pair接口:
SGI-STL中关于键值对的定义 :
template <class T1, class T2>
struct pair
{
typedef T1 first_type;
typedef T2 second_type;
T1 first;
T2 second;
pair() : first(T1()), second(T2())
{}
pair(const T1 &a, const T2 &b) : first(a), second(b)
{}
};
下面用pair接受返回值示例:
#include <iostream>
using namespace std;
#include <set>
void test_set()
{
set<int> s;
s.insert(5);
s.insert(2);
s.insert(6);
s.insert(3);
s.insert(1);
s.insert(3);
s.insert(3);
s.insert(3);
s.insert(5);
s.insert(6);
auto ret1 = s.insert(5);
cout << ret1.second << endl;
pair<set<int>::iterator, bool> ret2 = s.insert(5);
cout << ret2.second << endl;
pair<set<int>::iterator, bool> ret3 = s.insert(4);
cout << ret3.second << endl;
}
int main()
{
test_set();
return 0;
}
接收结果:
从结果可以看出,插入成功返回 1 ,插入失败返回 0 。
3.2.2 find
find接口简介:
返回值介绍:
find返回的就是迭代器 !!
当想在set中删除值时,也可以通过find来删除:
#include <iostream>
using namespace std;
#include <set>
void test_set()
{
set<int> s;
s.insert(5);
s.insert(2);
s.insert(6);
s.insert(3);
s.insert(1);
s.erase(1); //直接删除
set<int>::iterator it = s.find(2); //find函数删除
if (it != s.end())
{
s.erase(it);
}
for (auto e : s)
{
cout << e << " ";
}
}
int main()
{
test_set();
return 0;
}
删除结果:
如果查找的不存在,则程序会崩溃掉!!
#include <iostream>
using namespace std;
#include <set>
void test_set()
{
set<int> s;
s.insert(5);
s.insert(2);
s.insert(6);
s.insert(3);
s.insert(1);
set<int>::iterator it = s.find(30);
s.erase(it);
for (auto e : s)
{
cout << e << " ";
}
}
int main()
{
test_set();
return 0;
}
结果:
这里用的是vscode为显示任何效果,如果用vs,则会显示程序崩溃窗口!!
当夜也有解决方法:
#include <iostream>
using namespace std;
#include <set>
void test_set()
{
set<int> s;
s.insert(5);
s.insert(2);
s.insert(6);
s.insert(3);
s.insert(1);
set<int>::iterator it = s.find(30);
if (it != s.end())
{
s.erase(it);
}
for (auto e : s)
{
cout << e << " ";
}
}
int main()
{
test_set();
return 0;
}
3.2.3 lower_bound
给一个值返回一个迭代器!!
#include <iostream>
using namespace std;
#include <set>
void test_set()
{
set<int> myset;
set<int>::iterator itlow, itup;
for (int i = 1; i < 10; i++)
{
myset.insert(i * 10); //10 20 30 40 50 60 70 80 90
}
itlow = myset.lower_bound(25); //>= val值的位置的iterator
itup = myset.upper_bound(60); //> val值的位置的iterator
myset.erase(itlow, itup); //取值范围[25,60]
for(auto e : myset)
{
cout << e << " ";
}
cout << endl;
}
int main()
{
test_set();
return 0;
}
返回结果:
注意: set不支持修改!!!其余的功能就是迭代器的正常调用!!
3.3 multiset
3.3.1 multiset的介绍
multiset文档介绍
[翻译]:
-
multiset是按照特定顺序存储元素的容器,其中元素是可以重复的。
-
在multiset中,元素的value也会识别它(因为multiset中本身存储的就是<value, value>组成的键值对,因此value本身就是key,key就是value,类型为T). multiset元素的值不能在容器中进行修改(因为元素总是const的),但可以从容器中插入或删除。
-
在内部,multiset中的元素总是按照其内部比较规则(类型比较)所指示的特定严格弱排序准则进行排序。
-
multiset容器通过key访问单个元素的速度通常比unordered_multiset容器慢,但当使用迭代器遍历时会得到一个有序序列。
-
multiset底层结构为二叉搜索树(红黑树)。
注意:
-
multiset中再底层中存储的是<value, value>的键值对
-
mtltiset的插入接口中只需要插入即可
-
与set的区别是,multiset中的元素可以重复,set是中value是唯一的
-
使用迭代器对multiset中的元素进行遍历,可以得到有序的序列
3.3.2 multiset的使用
此处只简单演示set与multiset的不同,其他接口接口与set相同:
#include <iostream>
using namespace std;
#include <set>
void test_set()
{
set<int> s;
s.insert(5);
s.insert(2);
s.insert(6);
s.insert(3);
s.insert(1);
multiset<int>::iterator it = s.begin();
while(it != s.end())
{
cout << *it << " ";
++it;
}
cout << endl;
}
int main()
{
test_set();
return 0;
}
运行结果:
从结果看来,multiset的功能:排序 + 去重 !!!
3.3.3 find
#include <iostream>
using namespace std;
#include <set>
void test_set()
{
multiset<int> s;
s.insert(5);
s.insert(2);
s.insert(6);
s.insert(3);
s.insert(1);
s.insert(2);
s.insert(5);
s.insert(2);
s.insert(5);
multiset<int>::iterator it = s.begin();
while(it != s.end())
{
cout << *it << " ";
++it;
}
cout << endl;
//如果有多个值,find返回中序第一个val
it = s.find(2);
while(it != s.end())
{
cout << *it << " ";
++it;
}
cout << endl;
}
int main()
{
test_set();
return 0;
}
调用结果:
3.3.4 equal_range
实例:
#include <iostream>
using namespace std;
#include <set>
void test_set()
{
multiset<int> s;
s.insert(5);
s.insert(2);
s.insert(6);
s.insert(3);
s.insert(1);
s.insert(2);
s.insert(5);
s.insert(2);
s.insert(5);
cout << "原序列: ";
for (auto e : s)
{
cout << e << " ";
}
cout << endl;
cout << "现序列: ";
auto ret = s.equal_range(2);
s.erase(ret.first, ret.second);
for(auto e : s)
{
cout << e << " ";
}
cout << endl;
}
int main()
{
test_set();
return 0;
}
调用结果:
其中,first 和 second 的意义就是:
- first:>=val值
- second:>val值
由此可见,其功能就是删除所指定查找的所有重复的val值。
3.3.5 erase
在multiset中的使用:
#include <iostream>
using namespace std;
#include <set>
void test_set()
{
multiset<int> s;
s.insert(5);
s.insert(2);
s.insert(6);
s.insert(3);
s.insert(1);
s.insert(2);
s.insert(5);
s.insert(2);
s.insert(5);
cout << "原序列: ";
for (auto e : s)
{
cout << e << " ";
}
cout << endl;
cout << "删除个数: ";
size_t n = s.erase(2);
cout << n << endl;
cout << "现序列: ";
for(auto e : s)
{
cout << e << " ";
}
cout << endl;
}
int main()
{
test_set();
return 0;
}
删除结果: