STL容器
STL容器概述
c++ STL中的容器包括顺序容器和关联容器两大类,顺序容器包括6个基本容器【vector(向量)、string(char类型向量)、deque(双端队列)、list(双向链表)、forward_list(单链表)、array(数组)】和3个由基本容器衍生出的适配器【stack(栈)、queue(队列)、priority_queue(优先级队列)】,其中stack和queue基于deque实现,priority_queue基于vector实现。关联容器包括4个有序关联容器【set、map、multiset、multimap】和4个无序关联容器【unordered_set、unordered_map、unordered_multiset、unordered_multimap】,其中有序关联容器由红黑树实现,无序关联容器由哈希技术实现。
顺序容器与关联容器最根本的不同:顺序容器中元素的顺序与其加入容器时的位置相对应,与关键字无关;关联容器中元素的位置由其关键字的值决定。
关联容器之set与map
特点与差异
1.set内元素即为关键字,而map的元素为键值对(pair类型的对象)。
2.set内不能有重复的关键字,map内不能有重复关键字的pair。
3.set和map元素的关键字不能被修改,即set每个元素都不能修改,因为set的元素就是关键字,map里的每个pair的frist成员不能被修改,但其second成员可以被修改。
4.set和map内的元素会自动排序(RBTree维持有序性和平衡性)
注:例如map<string,int>类型的对象里面的元素为pair<const string,int>类型的对象、set< int> 类型的对象里面的元素为const int的常量。
5.set和map内设置了三个类型别名key_type、mapped_type、value_type来分别表示关键字的类型、关键字所关联(映射)的类型、元素的类型。set没有mapped_type。例如:
set< int >::key_type为const int
set< int >::value_type为const int
map<string,int>::key_type为const string
map<string,int>::mapped_type为int
map<string,int>::value_type为pair<const string,int>
定义与初始化
由于STL中所有容器本质均为类模板,故set和map在初始化的时候需要提供模板实参来引导编译器生成一个特定类型set和map的类,然后再调用构造函数或拷贝构造函数生成对应的对象。一般有六种定义方式。
//set与map的六种定义(构造)方式
//默认构造函数,构造空容器
set<int> s1;
map<string, int> m1;
//值初始化(列表初始化)
set<int> s2 = { 5,4,3,2,1 };
map<string, int> m2 = { {"xiaoming",18},{"xiaohua",19},{"xiaowang",20} };
//值范围(给定一对迭代器)初始化
vector<int> v1 = { 1,2,3,4,5,6,7 };
set<int> s3(v1.begin(),v1.end());
map<string, int> m3(m2.begin(),m2.end());
//构造函数,构造指定容器的副本
set<int> s4(s2);
map<string, int> m4(m2);
//拷贝构造函数,构造指定容器的副本
set<int> s5 = s2;
map<string, int> m5 = m2;
迭代器
共有八个获取迭代器的函数:begin, end, rbegin,rend 以及对应的 cbegin, cend, crbegin,crend。
对于set来说,均返回const_iterator类型的迭代器。
对于非const的map来说,前者返回iterator,后者返回const_iterator。
对于const的map来说,均返回const_iterator。
map<int,int> mmap;
const map<int,int> const_mmap;
mmap.begin(); //返回iterator
mmap.cbegin(); //返回const_iterator
const_mmap.begin(); //返回const_iterator
const_mmap.cbegin(); //返回const_iterator
按关键字查找
由于set和map底层实现均为RBTree(基于BST实现),查找时会从树根开始逐步向下查找(比较当前结点里的关键字值是否等于要查找的值,若相等则找到,否则根据要查找的值是小于结点值还是大于结点值选择检查其左孩子或右孩子),n个结点的RBTree树高为log(n),故最坏情况下的时间复杂度为log(n)。
set和map用于查找的函数均有五个:count、find、lower_bound、upper_bound、equal_range。
除了count,其余四个均有对应的尾后const版本。
其中count函数查找成功返回1,查找失败返回0;其余查找成功返回指向找到元素的迭代器,查找失败返回尾后迭代器
size_type count(const key_type& _Keyval) const
iterator find(const key_type& _Keyval)
const_iterator find(const key_type& _Keyval) const
iterator lower_bound(const key_type& _Keyval)
const_iterator lower_bound(const key_type& _Keyval) const
iterator upper_bound(const key_type& _Keyval)
const_iterator upper_bound(const key_type& _Keyval) const
pair<iterator, iterator> equal_range(const key_type& _Keyval)
pair<const_iterator, const_iterator> equal_range(const key_type& _Keyval) const
set的查找
int k1 = 7;
if (s2.find(k1) == s2.end())
cout << "关键字" << k1 << "不在set内" << endl;
else
cout << "关键字" << k1 << "在set内" << endl;
if (s2.count(k1) == 0)
cout << "关键字" << k1 << "不在set内" << endl;
else
cout << "关键字" << k1 << "在set内" << endl;
map的查找
string k2 = "xxx";
if (m2.find(k2) == m2.end())
cout << "map内不存在关键字为" << k2 << "的pair" << endl;
else
cout << "map内存在关键字为" << k2 << "的pair,其值为" << (m2.find(k2))->second<<endl;
if (m2.count(k2) == 0)
cout << "map内不存在关键字为" << k2 << "的pair" << endl;
else
cout << "map内存在关键字为" << k2 << "的pair" << endl;
修改
set的修改
set内元素为const类型,无法修改。
map的修改
map不能修改关键字,只能修改对应关键字的映射值。
map<string,int>::iterator it = m2.find("zhangsan");
(*it).second = 99;
it->second = 999;
插入
插入与查找一样,都有log(n)的最坏时间复杂度。首先从RBTree的根结点开始往下搜索插入位置,然后进行插入,若插入后破坏了RBTree平衡性,则进行平衡性调整。
1.插入单个元素
pair<iterator, bool> insert(const value_type& _Val)
2.插入一对迭代器范围内的若干元素
void insert(_Iter _First, _Iter _Last)
set的插入
//插入单个元素(关键字)
int k3 = 5;
pair<set<int>::iterator, bool> res_pair2 = s2.insert(k3);
if (res_pair2.second == true)
cout << "k3插入成功,插入的k3为 "<<*(res_pair2.first)<<endl;
else
cout << "插入失败,set内已经存在关键字" << k3 << "了" << endl;
//插入若干元素
s2.insert({ 3,4,5,6,7 });
vector<int> v2 = { 3,4,5,6,7 };
s2.insert(v2.begin(), v2.end());
map的插入
//插入单个元素(一个对应类型的pair对象)
pair<string, int> pair1 = make_pair("xiaoming", 30);
pair<map<string,int>::iterator , bool> res_pair1 = m2.insert(pair1);
if (res_pair1.second == true)
cout << "pair1插入成功,插入的pair为 " << "{" << (res_pair1.first)->first << "," << (res_pair1.first)->second << "}" << endl;
else
cout << "插入失败,map内已经存在关键字为"<<pair1.first<<"的pair了" << endl;
//插入若干个元素(若干个对应类型的pair对象)
m2.insert({ {"xiaoming",18},{"xiaohua",19},{"xiaowang",20} });
vector<pair<string, int>> v3 = { {"xiaoming",18},{"xiaohua",19},{"xiaowang",20} };
m2.insert(v3.begin(),v3.end());
注:make_pair函数创建的pair1是pair<string,int>类型的对象,但用pair1做insert函数参数插入在m2中对应的pair为pair<const string,int>类型的对象。
删除
1.删除迭代器指向的元素,返回删除元素的下一个元素
iterator erase(const_iterator _Where) noexcept
2.删除一对迭代器范围内的若干元素,返回删除区间最后一个元素的下一个元素
iterator erase(const_iterator _First, const_iterator _Last) noexcept
3.删除指定关键字的所有元素,返回实际删除的元素数量(set和map一定返回1),返回0则表示set和map内没有此关键字的元素
size_type erase(const key_type& _Keyval) noexcept(noexcept(_Eqrange(_Keyval)))
注:erase的前两个版本无需搜索删除位置,因为参数(迭代器或迭代器对)已经指定了删除位置或删除区间。而erase的第三版本参数为待删除元素的关键字,所以要先搜索,找到待删除元素的位置。
set的删除
s2.erase(s2.find(7));
s2.erase(s2.begin(), s2.end());
s2.erase(7);
map的删除
m2.erase(m2.find("xiaohua"));
m2.erase(m2.begin(), m2.end());
m2.erase("xiaohua");