目录
一、基础知识
1、序列式容器和关联式容器
- 序列式容器:底层是线性数据结构,且存储的是元素本身,称为序列式容器。例如:vector string list deque等。
- 关联式容器:底层数据结构存储的是<key,value>的键值对,在数据检索时比序列是容器效率更高。
2、键值对
用来表示具有一一对应关系的一种结构,该结构中一般只包含两个成员变量key和value,key代表键值,value表示与key对应的信息。
键值对的定义:
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)
{}
};
3、键值对的使用
1)访问键值对中的元素
pair(K,V) val;
//访问类型为K的元素
val.first
//访问类型为V的元素
val.second
2)构造一个键值对
make_pair(Key,Value)
二、map和set的介绍和使用
1、set的介绍
set基本介绍
- set是按照一定次序存储元素的容器(排序+去重的key模型)
- 在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中的元素,可以得到有序序列(二叉搜索树,左子树比根节点小右子树比根节点大,按照中序遍历就可以得到有序序列)。
- 默认情况下set中的元素按照小于进行比较的。
- set中查找某个元素的时间复杂度为:log(n) (平衡二叉搜索树,查找的时间复杂度为树的高度次)
- set中的元素值是const的,不能进行修改。
2、set的使用
1)set的模板参数列表
- T存储的元素类型,底层实际存储的是<value,value>的键值对。
- Compare是比较方式,默认是小于。
- Alloc是set中元素空间的管理方式,即空间配置器。
2)构造函数和析构函数
函数原型:
空构造:explicit set (const key_compare& comp = key_compare(), const allocator_type& alloc = allocator_type());
迭代器区间构造:set (InputIterator first, InputIterator last, const key_compare& comp = key_compare(), const allocator_type& alloc = allocator_type());
拷贝构造:set (const set& x);
使用举例:
void print(set<int>::iterator begin,set<int>::iterator end)
{
while (begin != end)
{
std::cout << *begin << " ";
begin++;
}
std::cout << std::endl;
}
int main()
{
set<int> s1;//使用空构造构造一个空的set
//向s1中插入元素,使s1不在为空
s1.insert(5);
s1.insert(8);
s1.insert(6);
s1.insert(3);
s1.insert(9);
print(s1.begin(), s1.end());
//使用拷贝构造构造一个和s1具有相同元素的set s2
set<int> s2(s1);
print(s2.begin(),s2.end());
//使用迭代器区间构造构造一个和s1相同的set s3
set<int> s3(s1.begin(),s1.end());
print(s3.begin(), s3.end());
//使用数组、vector迭代器区间构造set s4、s5
int array[5] = {1,2,3,4,5};
vector<int> v(array,array+5);//使用数组构造vector
set<int> s4(array,array+5);
print(s4.begin(), s4.end());
set<int> s5(v.begin(),v.end());
print(s5.begin(), s5.end());
//使用list迭代器构造set
list<int> lt(array,array+5);
set<int> s6(lt.begin(),lt.end());
print(s6.begin(), s6.end());
//使用string迭代器构造
string str = "abcd";
set<char> s7(str.begin(),str.end());
auto it = s7.begin();
while (it != s7.end())
{
std::cout << *it << " ";
it++;
}
std::cout << std::endl;
return 0;
}
3)迭代器
函数原型:
正向迭代器
iterator begin(); const_iterator begin() const; iterator end(); const_iterator end() const;
反向迭代器
reverse_iterator rbegin(); const_reverse_iterator rbegin() const; reverse_iterator rend(); const_reverse_iterator rend() const;
正向const迭代器
const_iterator cbegin() const noexcept; const_iterator cend() const noexcept;
反向const迭代器
const_reverse_iterator crbegin() const noexcept; const_reverse_iterator crend() const noexcept;
使用举例:
int main()
{
set<int> s1;
s1.insert(5);
s1.insert(8);
s1.insert(6);
s1.insert(3);
s1.insert(9);
//正向迭代器遍历
set<int>::iterator it = s1.begin();
while (it != s1.end())
{
cout << *it << " ";
it++;
}
cout << endl;
//反向迭代器遍历
set<int>::reverse_iterator rit = s1.rbegin();
while (rit != s1.rend())
{
cout << *rit << " ";
rit++;
}
cout << endl;
return 0;
}
4)容量相关的接口
函数原型:
判空:bool empty() const; 计算size:size_type size() const; 计算max_size:size_type max_size() const;
使用示例:
int main()
{
set<int> s1;
s1.insert(5);
s1.insert(8);
s1.insert(6);
s1.insert(3);
s1.insert(9);
cout << s1.size() << " " << s1.max_size() << endl;
string str = "abcde";
set<char> s2(str.begin(),str.end());
cout << s2.size() << " " << s2.max_size() << endl;
return 0;
}
5)基本操作(增、删、查)
函数原型:
插入元素 iterator insert (iterator position, const value_type& val); pair<iterator,bool> insert (const value_type& val); template <class InputIterator> void insert (InputIterator first, InputIterator last); 删除元素 void erase (iterator position); size_type erase (const value_type& val); //交换 void swap (set& x); //查找 iterator find (const value_type& val) const; //统计次数(均为1) size_type count (const value_type& val) const;
使用举例:
void print(set<int>::iterator begin, set<int>::iterator end)
{
while (begin != end)
{
std::cout << *begin << " ";
begin++;
}
std::cout << std::endl;
}
int main()
{
set<int> s1;
s1.insert(5);
s1.insert(8);
s1.insert(6);
s1.insert(3);
s1.insert(9);
print(s1.begin(),s1.end());
//向s1中插入一个值
s1.insert(15);//set中没有和要插入的值相同的值
print(s1.begin(), s1.end());
s1.insert(3);//要插入的值set中已经存在
print(s1.begin(), s1.end());
//删除
s1.erase(15);
print(s1.begin(), s1.end());
//swap---交换两个set
set<int> s2;
s2.insert(50);
s2.insert(80);
s2.insert(60);
s2.insert(30);
s2.insert(90);
s1.swap(s2);
print(s1.begin(), s1.end());
print(s2.begin(), s2.end());
//查找
auto it = s1.find(30);
cout << *it << endl;
s1.erase(it);
print(s1.begin(), s1.end());
//count
cout << s1.count(50) << endl;
return 0;
}
3、map的介绍
- map是关联式容器,按照特定的次序进行存储(使用key值进行比较)由key和value组成的键值对。
-
在map中,键值key通常用于排序和惟一地标识元素,而值value中存储与此键值key关联的内容。键值key和值value的类型可能不同,并且在map的内部,key与value通过成员类型value_type绑定在一起, 为其取别名称为pair:typedef pair value_type;
-
在内部,map中的元素总是按照键值key进行比较排序的。
-
map中通过键值访问单个元素的速度通常比unordered_map容器慢,但map允许根据顺序对元素进行直接迭代(即对map中的元素进行迭代时,可以得到一个有序的序列)。
-
map支持下标访问符,即在[]中放入key,就可以找到与key对应的value。
-
map通常被实现为二叉搜索树(更准确的说:平衡二叉搜索树(红黑树))。
- map按照key值进行比较,因此map中的两个或两个以上元素的value值是可以相等的。
4、map的使用
1)模板类参数
- Key键值对中key的类型
- T键值对中value的类型
- Compare是比较方式,默认是小于。map中的元素是按照key来比较的,缺省情况下按照小于来比较,一般情况下(内置类型元素)该参数不需要传递,如果无法比较时(自定义类型),需要用户自己显式传递比较规则(一般情况下按照函数指针或者仿函数来传递)
- Alloc是set中元素空间的管理方式,即空间配置器。
2)构造函数
空构造:explicit map (const key_compare& comp = key_compare(), const allocator_type& alloc = allocator_type());
迭代器区间构造
template <class InputIterator>
map (InputIterator first, InputIterator last, const key_compare& comp = key_compare(), const allocator_type& alloc = allocator_type());
拷贝构造:map (const map& x);
3)迭代器
正向迭代器
iterator begin(); const_iterator begin() const; iterator end(); const_iterator end() const;
反向迭代器
reverse_iterator rend(); const_reverse_iterator rend() const; reverse_iterator rend(); const_reverse_iterator rend() const;
正向const迭代器
const_iterator cbegin() const noexcept; const_iterator cend() const noexcept;
反向const迭代器
const_reverse_iterator crbegin() const noexcept; const_reverse_iterator crend() const noexcept;
4)容量相关
判空:bool empty() const;
计算size:size_type size() const;
计算max_size:size_type max_size() const;
5)operator[]
mapped_type& operator[] (const key_type& k); //函数功能:返回key对应的value,如果不存在则插入 A call to this function is equivalent to:(*((this->insert(make_pair(k,mapped_type()))).first)).second 注意:在元素访问时,有一个与operator[]类似的操作at()(该函数不常用)函数,都是通过key找到与key对应的value然后返回其引用,不同的是:当key不存在时,operator[]用默认value与key构造键值对 然后插入,返回该默认value,at()函数直接抛异常。
int main()
{
map<char, int> m1;//构造一个空的map
/*
operator[]的原理是:
用<key, T()>构造一个键值对,然后调用insert()函数将该键值对插入到map中
如果key已经存在,插入失败,insert函数返回该key所在位置的迭代器
如果key不存在,插入成功,insert函数返回新插入元素所在位置的迭代器
operator[]函数最后将insert返回值键值对中的value返回*/
m1['a'] = 65;
return 0;
}
6)基本操作(增、删、该、查)
插入
按照键值对插入:pair<iterator,bool> insert (const value_type& val);
迭代器区间插入:iterator insert (iterator position, const value_type& val);
迭代器区间插入:template <class InputIterator> void insert (InputIterator first, InputIterator last);
删除
迭代器删除:void erase (iterator position);
key值删除:size_type erase (const key_type& k);
迭代器区间删除:void erase (iterator first, iterator last);
交换
void swap (map& x);
清空
void clear();
查找
iterator find (const key_type& k);
const_iterator find (const key_type& k) const;
统计
size_type count (const key_type& k) const;
使用举例
int main()
{
map<char, int> m1;//构造一个空的map
/*
operator[]的原理是:
用<key, T()>构造一个键值对,然后调用insert()函数将该键值对插入到map中
如果key已经存在,插入失败,insert函数返回该key所在位置的迭代器
如果key不存在,插入成功,insert函数返回新插入元素所在位置的迭代器
operator[]函数最后将insert返回值键值对中的value返回*/
m1['a'] = 65;
m1.insert(pair<char, int>('a', 65));
m1.insert(pair<char, int>('b', 66));
m1.insert(pair<char, int>('c', 67));
m1.insert(pair<char, int>('d', 68));
m1.insert(pair<char, int>('e', 69));
m1.insert(pair<char, int>('f', 70));
auto it = m1.find('a');//key值查找
//删除
m1.erase('b');//key值删除
m1.erase(it);//迭代器删除
m1.erase(m1.begin(),m1.end());//迭代器区间删除
//清空
m1.clear();
//拷贝构造
map<char, int> m2(m1);
m1.swap(m2);
return 0;
}
三、multiset和multimap的介绍和使用
1、multiset介绍
- multiset是按照某种特定顺序存储元素的容器,与set的区别是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中的元素进行遍历,可以得到有序的序列
-
multiset中的元素不能修改
-
在multiset中找某个元素,时间复杂度为
-
multiset的作用:可以对元素进行排序
2、multiset的使用
int main()
{
int array[] = {1,2,5,8,6,4,3,9,5,7,6,8,10,1};
//使用迭代器区间进行构造
multiset<int> ms1(array, array + 14);//允许存在重复元素
return 0;
}
3、multimap介绍
- Multimaps是关联式容器,它按照特定的顺序,存储由key和value映射成的键值对<key, value>,其中多个键值对之间的key是可以重复的。
- 在multimap中,通常按照key排序和惟一地标识元素,而映射的value存储与key关联的内容。key和value的类型可能不同,通过multimap内部的成员类型value_type组合在一起,value_type是组合key和value的键值对:
- typedef pair<const Key, T> value_type;
- 在内部,multimap中的元素总是通过其内部比较对象,按照指定的特定严格弱排序标准对key进行排序的。
- multimap通过key访问单个元素的速度通常比unordered_multimap容器慢,但是使用迭代器直接遍历multimap中的元素可以得到关于key有序的序列。
- multimap在底层用二叉搜索树(红黑树)来实现。
注意:map和multimap唯一的区别就在于multimap中允许存在重复的键值对,而map中不能。还需注意的是multimap中没有重载[]。