一、关联式容器

我们曾经接触过的vector、list、deque等容器都被称之为序列式容器,因为其底层为线性序列的数据结构,里面存储的是元素本身。

关联式容器也是用来存储数据的,与序列式容器不同的是,里面存储的是

<key,value>结构的键值对,在数据检索时比序列式容器效率更高。

二、键值对

键值对用来表示具有一一对应关系的一种结构,该结构中一般只包含两个成员key和value,key代表键值,value表示与key对应的信息。比如:现在要建立一个英汉互译的字典,那该字典中必然有英文单词与其对应的中文含义,而且,英文单词与其中文含义是一一对应的关系,即通过该英文单词,在字典中就可以找到与其对应的中文含义。

SGI-STL中关于键值的定义如下:

template <class T1, class T2>
struct pair
{
typedef T1 frist_type;
typedef T2 second_type;

T1 frist;
T2 second;
pair(): frist(T1()),second(T2())
{}
 
pair(const T1& a,const T2& b): frist(a),second(b)
{}
};

三、树形结构的关联式容器

根据使用场景的不同,STL总共实现了两种不同结构的关联式容器:树形结构和哈希结构。树形结构的关联式容器主要有四种:map、set、multimap、

multiset。这四个容器的共同点是:使用平衡搜索树(即红黑树)作为其底层结构,容器中的元素是一个有序的序列。

1、set

(1)set的介绍

set文档介绍

①set是按照一定次序存储元素的容器。

②在set中,元素的value也标识它(value就是key,类型为T),并且每个value必须是唯一的。set中的元素不能在容器中修改(元素总是const),但是可以从容器中插入或者删除它们。

③在内部,set中的元素总是按照其内部比较对象(类型为Compare)所指示的特定严格弱排序准则进行排序。

④set容器通过key访问单个元素的速度通常比unordered_set容器慢,但是它们允许根据子集的顺序对直接迭代子集。

⑤set在底层是用二叉搜索树(红黑树)实现的。

注意:

  1. 与map/multimap不同,map/multimap中存储的是真正的键值对<key,value>,set中只放value,但是在底层实际存放的是由<value,value>构成的键值对。
  2. set中插入元素时,只需要插入value即可,不需要构造键值对。
  3. set中的元素不可以重复(因此可以用set去重)。
  4. 使用set的迭代器遍历set中的元素,可以得到有序序列。
  5. set中的元素默认按照小于来比较。
  6. set中查找某个元素,时间复杂度为log2^n。
  7. set中的元素不允许修改。

(2)set的使用

①set的模板参数列表

map与set_map

T:set中存放元素的类型,实际底层中存储<value,value>的键值对。

Compare:比较器的类型,缺省情况下按照小于来比较,一般情况下(内置类型元素)该参数不需要传递,如果无法比较时(自定义类型),需要用户自己显式传递比较规则(一般情况下按照函数指针或者仿函数来传递)

Alloc:set中元素空间的管理方式,使用STL提供的空间配置管理器。

②set的构造

constructor

函数声明

功能介绍

set(cosnt Compare& comp=Compare(),const Allocator& alloc=Allocator() );

构造空的set

set(InputIterator frist,InputIterator last,const Compare&  comp=Compare(),const Allocator& alloc=Allocator() );

用(frist,last)迭代器区间的元素构造set

set(const set<Key,Compare,Allocator>& x);

set的拷贝构造

③set的迭代器

函数声明

功能介绍

iterator begin();

返回set中起始位置元素的迭代器

iterator end();

返回set中最后一个元素后面的迭代器

reverse_iterator rbegin();

返回set第一个元素的反向迭代器

reverse_iterator rend();

返回set最后一个元素下一个位置的反向迭代器

const_iterator cbegin()const;

返回set中起始位置元素的const迭代器

const_iterator cend()const;

返回set中最后一个元素后面的const迭代器

const_reverse_iterator crbegin()const;

返回set第一个元素的反向const迭代器

const_reverse_iterator crend()const;

返回set最后一个元素下一个位置的反向const迭代器

set的容量

函数声明

功能介绍

bool empty() const;

检测set是否为空,空返回true,否则返回true

size_type size() const;

返回set中有效元素的个数

⑤set的修改

函数声明

功能介绍

pair<iterator,bool> insert(const value_type& x);

在set中插入元素x,实际插入的是<x,x>构成的键值对,如果插入成功,返回<该元素在set中的位置,true>,如果插入失败,说明在set中已经存在,返回<该元素在set中的位置,false>

void erase(iterator position);

删除set中position位置上的元素


size_type erase(const key_type& x);

删除set中值为x的元素,返回删除的元素的个数

void erase(iterator frist,iterator last);

删除set中[frist,last)区间中的元素

void swap(set<Key,Compare,Allocator>& st);

交换set中的元素

void clear();

将set中的元素清空

iterator find(const key_type& x);

返回set中值为x的元素的位置的迭代器,如果没找到,返回end迭代器

size_type count(const key_type& x)const;

返回set中值为x的元素的个数


iterator lower_bound (const value_type& val) const;

返回第一个大于等于val值位置的迭代器

iterator upper_bound (const value_type& val) const;

返回第一个大于val值位置的迭代器

pair<iterator,iterator> equal_range (const value_type& val) const;

返回<第一个大于等于val值位置的迭代器,第一个大于val值位置的迭代器>

⑥补充

  • insert的返回类型时pair<iterator,bool>,这是一个模板类,它的第一个参数frist,类型为iterator,另一个参数second类型为bool。
  • 可以用count来判断一个值在不在set里。

⑦set的使用举例

void Test1()
{
	vector<int> v = { 5,4,2,2,3,5,2,1,8,7,9,10,4 ,6 };//有重复元素
	set<int> s(v.begin(), v.end());
	for (auto a : s)//打印set里的元素
	{
		cout << a << " ";
	}//打印出来的元素没有重复,并且按升序排列
	cout << endl;
	set<int>::iterator it = s.find(10);//找到10
	if (it != s.end())//如果成功找到了
	{
		s.erase(it);//删除它
	}
	it = s.lower_bound(5);//打印大于等于5的值
	while (it != s.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
	it = s.upper_bound(5);//打印大于5的值
	while (it != s.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
}

map与set_map_02

2、multiset

(1)multiset的介绍

multiset文档介绍

multiset与set主要的差别就在于它允许插入重复的元素。

(2)multiset的使用

multiset和set的接口基本相同

void Test1()
{
	vector<int> v = { 5,4,2,2,3,5,2,1,8,7,9,10,4 ,6 };//有重复元素
	multiset<int> s(v.begin(), v.end());
	for (auto a : s)//打印set里的元素
	{
		cout << a << " ";
	}//打印出来的元素有重复
	cout << endl;
	set<int>::iterator it = s.find(10);
	//multiset里的find查找的元素如果有重复,只会找中序遍历的第一个元素。
	if (it != s.end())//如果成功找到了
	{
		s.erase(it);//删除它
	}
	auto ret = s.equal_range(2);
	//multiset里的equal_range可以帮助我们删除相同元素
	//当然,直接erase(2)效果也是相通的
	s.erase(ret.first, ret.second);
	for (auto a : s)//打印set里的元素
	{
		cout << a << " ";
	}
	cout << endl;
}

map与set_set_03

3、map

(1)map的介绍

map的文档介绍

①map是关联式容器,它按照特定的次序(按照key值来比较)存储有键值key和值value组合而成的元素。

②在map中,键值key通常用于排序和唯一地标识元素,而值value中存储与键值key关联的内容。键值key和值value的类型可能不同,并且在map内部,key与value通过成员类型value_type绑定在一起。

typedef pair<const key,T> value_type;

③在内部,map中的元素总是按照键值key进行比较排序的。

④map中通过键值访问单个元素的速度比unordered_map容器慢,但map允许根据顺序对元素进行直接迭代。

⑤map支持下表访问,即在[]中放入key,就可以找到与key对应的value。

⑥map在底层是用二叉搜索树(红黑树)实现的。

(2)map的使用

①map的模板参数说明

map与set_map_04

Key:键值对中key的类型。

T:键值对中value的类型。

Compare:比较器的类型,map中的元素是按照key来比较的,缺省情况下按照小于来比较,一般情况下(内置类型元素)该参数不需要传递,如果无法比较时(自定义类型),需要用户自己显式传递比较规则(一般情况下按照函数指针或者仿函数来传递)

Alloc:通过空间配置器来申请底层空间,不需要用户传递,,除非用户不想使用标准库提供的空间配置器。

②map的构造

constructor

函数声明

功能介绍

map (const key_compare& comp = key_compare(), const allocator_type& alloc = allocator_type());

构造一个空的map

map (InputIterator first, InputIterator last, const key_compare& comp = key_compare(), const allocator_type& alloc = allocator_type());

用(frist,last)迭代器区间的元素构造map

map (const map& x);

map的拷贝构造

③map的迭代器

函数声明

功能介绍

iterator begin();

返回map中起始位置元素的迭代器

iterator end();

返回map中最后一个元素后面的迭代器

reverse_iterator rbegin();

返回map第一个元素的反向迭代器

reverse_iterator rend();

返回map最后一个元素下一个位置的反向迭代器

const_iterator cbegin()const;

返回map中起始位置元素的const迭代器

const_iterator cend()const;

返回map中最后一个元素后面的const迭代器

const_reverse_iterator crbegin()const;

返回map第一个元素的反向const迭代器

const_reverse_iterator crend()const;

返回map最后一个元素下一个位置的反向const迭代器

④map的容量

函数声明

功能介绍

bool empty() const;

检测map是否为空,空返回true,否则返回true

size_type size() const;

返回map中有效元素的个数

⑤map中元素的修改

函数声明

功能简介

pair<iterator,bool> insert (const value_type& val);

在map中插入键值对val,注意val是一个键值对,返回值也是键值对:iterator代表新插入元素的位置,bool表示是否插入成功

void erase (iterator position);

删除position位置上的元素

size_type erase (const key_type& k);

删除键值为k的元素

void erase (iterator first, iterator last);

删除set中[frist,last)区间中的元素

void swap (map& x);

交换两个map中的元素

void clear();

将map中的元素清空

iterator find (const key_type& k);

在map中查找key为k的元素,找到返回该元素的位置的迭代器,否则返回end

size_type count (const key_type& k) const;

返回map中key为k的元素的个数

iterator lower_bound (const key_type& k);

返回第一个大于等于k值位置的迭代器

iterator upper_bound (const key_type& k);

返回第一个大于k值位置的迭代器

pair<iterator,iterator> equal_range (const key_type& k);

返回<第一个大于等于val值位置的迭代器,第一个大于k值位置的迭代器>

⑥map的元素访问

函数声明

功能介绍

mapped_type& operator[] (const key_type& k);

返回map中key对应的value

operator[]底层是用insert来实现的

map与set_map_05

map与set_map_06

insert返回的是一个pair,pair::frist是一个迭代器它指向新插入的元素(插入成功),要么指向和key相同的的元素(插入失败,key已经存在)。pair::second是true或者false

也就是说我们在使用operator[]时,即使没有找到,也会把这个key插入进去。它有插入、修改、查找的功能。

⑦补充

  • inser插入的元素的类型是pair<const key_type,mapped_type>

map与set_map_06

虽然我们定义的value_type里的pair的第一个模板参数是const类型的,但是我们在传参不必传const类型,pair内部会转化为const类型。

map<string, string> m;
m.insert(pair<string, string>("sort", "排序"));

之所以可以在内部可以转化为const类型,是因为它的拷贝构造函数是函数模板。

map与set_set_08

拷贝构造函数里初始化列表为:frist(pr.frist),second(pr.second)

pr的frist即使是非const的,也可以用来拷贝构造给const的frist。

map与set_set_09

pair里有一个方法叫函数模板make_pair,函数模板可以自己推演出模板参数,所以,我们一般这样使用插入元素

map<string, string> m;
m.insert(make_pair("sort", "排序"));

⑧map的使用举例

void Test2()
{
	map<string, string> m;
	m.insert(make_pair("sort", "排序"));
	m.insert(make_pair("insert", "插入"));
	m.insert(make_pair("erase", "删除"));
	m.insert(make_pair("swap", "交换"));
	m.insert(make_pair("find", "查找"));

	for (const auto& a : m)
	{
		cout << a.first << ":" << a.second << endl;
	}
}

map与set_map_10

void Test3()
{
	//统计水果出现的次数
	vector<string> v = { "西瓜","西瓜","水蜜桃","火龙果","香蕉","水蜜桃","火龙果","西瓜","水蜜桃","香蕉","西瓜","火龙果", };
	map<string, int> m;
	for (const auto& a : v)//插入
	{
		m[a]++;
	}
	for (const auto& a : m)//查看出现次数
	{
		cout << a.first << ": " << a.second << endl;
	}
}

map与set_map_11

4、multimap

(1)multimap的介绍

multimap文档介绍

multiset与set主要的差别就在于它允许插入重复的元素,以及它没有实现operator[]。其他接口与map也没有什么差别。



完结。。。。。