键值对
用来表示具有一一对应关系的一种结构,该结构中一般只包含两个成员变量<key,value>,key代表键值,value表示与key对应的信息。比如:现在要建立一个英汉互译的字典,那该字典中必然有英文单词与其对应的中文含义,而且,英文单词与其中文含义是一一对应的关系,即通过该应该单词,在词典中就可以找到与其对应的中文含义。
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)
{}
};
set
概念
C++ STL 之所以得到广泛的赞誉,也被很多人使用,不只是提供了像vector, string, list等方便的容器,更重要的是STL封装了许多复杂的数据结构算法和大量常用数据结构操作。vector封装数组,list封装了链表,map和set封装了二叉树等,在封装这些数据结构的时候,STL按照程序员的使用习惯,以成员函数方式提供的常用操作,如:插入、排序、删除、查找等。让用户在STL使用过程中,并不会感到陌生。
关于set,必须说明的是set关联式容器。set作为一个容器也是用来存储同一数据类型的数据类型,并且能从一个数据集合中取出数据,在set中每个元素的值都唯一,而且系统能根据元素的值自动进行排序。应该注意的是set中数元素的值不能直接被改变。C++ STL中标准关联容器set, multiset, map, multimap内部采用的就是一种非常高效的平衡检索二叉树:红黑树,也成为RB树(Red-Black Tree)。RB树的统计性能要好于一般平衡二叉树,所以被STL选择作为了关联容器的内部结构。
特点
- 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中查找某个元素,时间复杂度为:(LogN)
- set中的元素不允许修改。
- set中的底层使用红黑树
用法
#include<iostream>
#include <set>
using namespace std;
int main()
{
//1.set 存储单个数据
// 用数组array中的元素构造set
int array[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0, 1, 3, 3, 7, 9, 2, 4, 6, 8, 3 };
set<int> s(array, array + sizeof(array) / sizeof(array[0])); //去重加排升序
//multiset<int> s(array, array + sizeof(array) / sizeof(array[0])); //不去重加排升序
cout << s.size() << endl;
//正向打印set中的元素,从打印结果中可以看出:set可去重
for (auto e : s)
cout << e << " ";
cout << endl;
//使用反向迭代器逆向打印set中的元素
for (auto it = s.rbegin(); it != s.rend(); ++it)
cout << *it << " ";
cout << endl;
//set中判断s中3是否出现过,multiset中判断3出现的次数
cout << s.count(3) << endl;
//2. set 存储 pair 数据
set<pair<string, string>> t;
t.insert(make_pair("string", "字符串"));
t.insert(make_pair("int", "整形"));
t.insert(make_pair("float", "浮点"));
auto e = t.find(make_pair("float", "浮点")); //pair 类型的数据返回的迭代器也是一个 pair
cout << e->first << "->" << e->second << endl;
}
set 的四种形式
- set ; 去重 + 排升序 ; (底层是 红黑树 )
- multiset ; 不去重 + 排升序 ; (底层是 红黑树 )
- unordered_set (也就是 hash_set,但是 unordered_map 在C++11的时候被引入标准库了,而hash_map没有,所以现在都用前者,二者其实是同一个东西); 去重 + hash 排序 ; (底层是 hash )
- unordered_multiset ; 不去重 + hash 排序; (底层是 hash )
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通常被实现为平衡二叉搜索树 (红黑树)
使用
#include<iostream>
#include<map>
#include<set>
#include<string>
using namespace std;
int main()
{
map<string, string> m;
m.insert(make_pair("banana", "香蕉"));
m.insert(make_pair("apple", "苹果"));
m.insert(make_pair("apple", "苹果"));
m.insert(make_pair("banana", "香蕉"));
m.insert(make_pair("pear", "梨"));
m.insert(make_pair("watermelon", "西瓜"));
m.insert(make_pair("watermelon", "西瓜"));
m.insert(make_pair("cherry", "樱桃"));
m.insert(make_pair("Orange", "橙子"));
m.insert(make_pair("Orange", "橙子"));
m.insert(make_pair("Grape", "葡萄"));
m.insert(make_pair("Grape", "葡萄"));
m.insert(make_pair("Grape", "葡萄"));
m.insert(make_pair("Grape", "葡萄"));
cout << m.size() << endl;
for (auto e : m)
{
cout << e.first << "-->" << e.second << " ";
}
cout << endl;
m["Peach"] = "桃子"; //为什么这里的 [] 也能够进行插入呢?
cout << m.size() << endl;
for (auto e : m)
{
cout << e.first << "-->" << e.second << " ";
}
cout << endl;
}
map 的 operator[]
- STL 中 operator[] 的实现
mapped_type& operator[] (const key_type& k)
{
return (*((this->insert(make_pair(k, mapped_type()))).first)).second;
}
要想搞懂 operator [] 的实现,先得来看 insert 的返回值
single element (1)
pair<iterator,bool> insert (const value_type& val);
with hint (2)
iterator insert (iterator position, const value_type& val);
- map对象中一个给定键只对应一个元素,也就是说 key_type(键) 和mapped_type(值) 是一一对应关系,如果试图插入的元素所对应的键已在容器中,则 insert 将不做任何操作;
- 单元素版本(1)返回一对 (pair),其成员为 -> first 设置为指向新插入的元素或映射中具有等效键的元素的迭代器。如果插入了新元素,则将 bool 设置为true;如果已存在等效键,则将 bool 设置为 false,且不改变原来的 mapped_type;
pair<map<string,string>::iterator,bool> ret = m.insert(make_pair("Grape", "葡萄"));
mapped_type& operator[] (const key_type& k)
{
return (*((this->insert(make_pair(k, mapped_type()))).first)).second;
}
用我们自己的方式去写就是
mapped_type& operator[] (const key_type& k)
{
pair<map<key_type,mapped_type>::iterator,bool> ret = m.insert(k,mapped_type());
return ret.first->second;
}
- pair 里面的两个元素的类型分别是:map<string,string >::iterator 这个指向新插入的元素或映射中具有等效键的元素的迭代器(这个迭代器其实也是一个 pair 对象的指针),和 bool 值。这个 pair 类型变量 ret 是insert 操作的返回值。
- 带有提示(2)的版本返回一个迭代器,该迭代器指向新插入的元素或映射中已具有等效键的元素。元素可插入位置的提示 (position)。如果 position 指向插入元素之前的元素,函数将优化其插入时间。请注意,这只是一个提示,并不强制将新元素插入到映射容器中的该位置(映射中的元素始终遵循特定的顺序,具体取决于它们的键;
map<string,string> my_map;
map<string,string>::iterator it = my_map.begin();
my_map.insert(it,make_pair("string","字符串")); //这里不是强制插入,关键看 key 是否已经存在
map 的四种形式
- map; 去重 + 排升序 ; (底层是红黑树)
- multimap ; 不去重 + 排升序 ; (底层是红黑树)
- unordered_map ; 去重 + hash 排序 ; (底层是 hash )
- unordered_multimap ; 不去重 + hash 排序 ; (底层是 hash )