什么是关联式容器:
关联式容器也是用来存储数据的,与序列式容器(vector, list, deque…)不同的是,关联式容器是通过key来存储和读取元素的,在数据检索时比序列式容器效率更高
键值对:
用来表示具有一一对应关系的一种结构,该结构中一般只包含两个成员变量key和value,key代表键值, value表示与key对应的信息, 这类似于字典中的一一对应关系.
实现构造键值对pair(粗略的实现, 主要是看整体结构):
#pragma once
namespace sock {
template<class T1, class T2>
struct pair {
T1 first;
T2 second;
pair() : first(T1()), second(T2()) {
}
pair(const T1& f, const T2& s)
: first(f), second(s) {
}
};
}
树型结构的关联式 容器主要有四种:map、set、multimap、multiset。这四种容器的共同点是:使用平衡搜索树(即红黑树) 作为其底层结果,容器中的元素是一个有序的序列。
set:
set的特性:
1.set的底层是红黑树, 按照一定次序来存储
2.其元素为key, 不允许修改, 并且是唯一的
3.默认中序遍历二叉搜索树, 结果为升序
set的简单使用:
key的唯一性:
int main() {
set<int> s;
s.insert(9);
s.insert(5);
s.insert(2);
s.insert(5);
s.insert(7);
set<int>::iterator sit = s.begin();
while (sit != s.end()) {
cout << *sit << " ";
++sit;
}
cout << endl;
return 0;
}
int main() {
set<int> s;
s.insert(9);
s.insert(5);
s.insert(2);
s.insert(5);
s.insert(7);
set<int>::iterator it = s.find(5);
s.erase(it);
set<int>::iterator sit = s.begin();
while (sit != s.end()) {
cout << *sit << " ";
++sit;
}
cout << endl;
return 0;
}
set中元素的插入就是在树中插入结点, 调整树结构的过程. 迭代器指针始终指向当前结点, 因为key是唯一的, 因此对于已经存在的key不再进行插入(可以通过s.count(key)来验证, 并且也能说明set中是否有该key值).
map:
map的特性:
1.map的底层是红黑树, 按照一定次序来存储
2.其元素为pair
3.默认中序遍历二叉搜索树, 结果为升序
map的简单使用:
key的唯一性:
int main() {
map<string, string> m;
m.insert(pair<string, string>("sort", "排序"));
m.insert(make_pair("search", "搜索"));
m.insert(make_pair("release", "发行"));
m.insert(make_pair("release", "释放"));
map<string, string>::iterator mit = m.begin();
while (mit != m.end()) {
cout << mit->first << ":" << mit->second << endl;
++mit;
}
return 0;
}
int main() {
map<string, string> m;
m.insert(pair<string, string>("sort", "排序"));
m.insert(make_pair("search", "搜索"));
m.insert(make_pair("release", "发行"));
m.insert(make_pair("release", "释放"));
// find(key) 返回指向map的迭代器类型
map<string, string>::iterator it = m.find("release");
m.erase(it);
map<string, string>::iterator mit = m.begin();
while (mit != m.end()) {
cout << mit->first << ":" << mit->second << endl;
++mit;
}
return 0;
}
key的唯一性与不可变性:
int main() {
map<string, string> m;
m.insert(make_pair("sort", "排序"));
m.insert(make_pair("search", "搜索"));
m.insert(make_pair("release", "发行"));
m.insert(make_pair("release", "释放"));
map<string, string>::iterator mit = m.begin();
while (mit != m.end()) {
//if (mit == m.find("search")) { //error
// mit->first = "find";
//}
cout << mit->first << ":" << mit->second << endl;
++mit;
}
return 0;
}
唯一性在上述上述代码中已经判断过了, 对于不可变性, mit->first = "find"这段代码会报错—"no viable overloaded ‘=’ ", 提示你不能对first进行赋值. 但是second却可以.
key的多样性:
在multiset和multimap中实现了key的多样性, 这也是multiset&multimap与set&map最显著的区别:
int main() {
int arr[] = {
9, 5, 5, 2, 7};
multiset<int> ms(arr, arr + sizeof(arr) / sizeof(int));
multiset<int>::iterator msit = ms.begin();
while (msit != ms.end()) {
cout << *msit << " ";
++msit;
}
cout << endl;
multimap<string, string> mm;
mm.insert(make_pair("sort", "排序"));
mm.insert(make_pair("search", "搜索"));
mm.insert(make_pair("release", "发行"));
mm.insert(make_pair("release", "释放"));
multimap<string, string>::iterator mmit = mm.begin();
while (mmit != mm.end()) {
cout << mmit->first << ":" << mmit->second << endl;
++mmit;
}
return 0;
}
int main() {
map<string, string> m;
m.insert(make_pair("sort", "排序"));
m.insert(make_pair("search", "搜索"));
m["release"] = "发行";
m["release"] = "释放";
map<string, string>::iterator mit = m.begin();
while (mit != m.end()) {
cout << mit->first << ":" << mit->second << endl;
++mit;
}
return 0;
}
operator[]与insert的效果是不同的, insert遇到重复的key则不进行插入, 而operator[] 是先进行insert(key, V())的操作, 然后将新的val赋给second.
operator[]的实现:
mapped_type& operator[](const key_type& key) {
// 如果key不存在, 则正常插入返回ret, 其iterator指向刚插入的pair
// 如果key存在, 则返回ret, 其iterator指向已存在的pair
pair<iterator, bool> ret = insert(make_pair(key, mapped_typed()));
return ret.first->second; // 返回val
}
静态的insert与erase:
insert:
//set&map
pair<iterator,bool> insert (const value_type& val);
iterator insert (iterator position, const value_type& val);
erase:
//set&map
void erase (iterator position);
size_type erase (const value_type& val);
与序列式容器不同的是, 对关联式容器进行插入删除操作, 会整体改变其内部的存储顺序, 这是由于底层是搜索二叉树(红黑树)的原因, 因此其插入或删除后的调整顺序是未知的, 一般不会在循环迭代中进行此类操作.
一句话来说, 就是insert和erase会影响整个树的布局, 一般不会边迭代边做insert和erase操作.