C++复习笔记19

        关联式容器 也是用来存储数据的,与序列式容器不同的是,其 里面存储的是 <key, value> 结构的键值对,在数据检索时比序列式容器效率更高
        用来表示具有一一对应关系的一种结构,该结构中一般只包含两个成员变量 key value key 代表键值, value 表示与 key 对应的信息 。比如:现在要建立一个英汉互译的字典,那该字典中必然有英文单词与其对应的中文含义,而且,英文单词与其中文含义是一一对应的关系,即通过该应该单词,在词典中就可以找到与 其对应的中文含义。
键值对代码示例:
#include<iostream>
#include<string>
using namespace std;

//template<class _Ty1,class _Ty2>
//struct KeyValue
//{
//	_Ty1 first;
//	_Ty2 second;
//};

void test01()
{
	//值对
	pair<int, char>p1; //first second
	cout << p1.first << " " << p1.second << endl;
	pair<int, int>p2(1, 100);
	cout << p2.first << " " << p2.second << endl;
	pair<int, string>p3 = make_pair(10, "lalala");//make_pair的使用
	cout << p3.first<<" " << p3.second << endl;
}

void main()
{
	test01();
}
       根据应用场景的不桶, STL 总共实现了两种不同结构的管理式容器:树型结构与哈希结构。 树型结构的关联式容器主要有四种: map set multimap multiset 。这四种容器的共同点是:使用平衡搜索树 ( 即红黑树 ) 作为其底层结果,容器中的元素是一个有序的序列。
map容器:
    1. map 是关联容器,它按照特定的次序 ( 按照 key 来比较 ) 存储由键值 key 和值 value 组合而成的元素。
     2. map 中,键值 key 通常用于排序和惟一地标识元素,而值 value 中存储与此键值 key 关联的内容。键值 key 和值 value 的类型可能不同,并且在 map 的内部, key value 通过成员类型 value_type 绑定在一起, 为其取别名称为 pair: typedef pair value_type;
     3. 在内部, map 中的元素总是按照键值 key 进行比较排序的。
     4. map 中通过键值访问单个元素的速度通常比 unordered_map 容器慢,但 map 允许根据顺序对元素进行 直接迭代 ( 即对 map 中的元素进行迭代时,可以得到一个有序的序列 )
     5. map 支持下标访问符,即在 [] 中放入 key ,就可以找到与 key 对应的 value
     6. map 通常被实现为二叉搜索树 ( 更准确的说:平衡二叉搜索树 ( 红黑树 )(key值符合红黑树(也是二叉搜索树))中序访问红黑树)。
key: 键值对中 key 的类型
T : 键值对中 value 的类型
       Compare: 比较器的类型, map 中的元素是按照 key 来比较的,缺省情况下按照小于来比较,一般情况 ( 内置类型元素 ) 该参数不需要传递,如果无法比较时 ( 自定义类型 ) ,需要用户自己显式传递比较规则 ( 一般情况下按照函数指针或者仿函数来传递)。
       Alloc :通过空间配置器来申请底层空间,不需要用户传递,除非用户不想使用标准库提供的空间配置器注意:在使用 map 时,需要包含头文件。

 

       注意:在元素访问时,有一个与operator[]类似的操作at()(该函数不常用)函数,都是通过key找到与key对应的value然后返回其引用,不同的是:当key不存在时,operator[]用默认value与key构造键值对 然后插入,返回该默认value,at()函数直接抛异常
1. map 中的的元素是键值对
2. map 中的 key 是唯一的,并且不能修改
3. 默认按照小于的方式对 key 进行比较
4. map 中的元素如果用迭代器去遍历,可以得到一个有序的序列
5. map 的底层为平衡搜索树 ( 红黑树 ) ,查找效率比较高
6. 支持 [] 操作符, operator[] 中实际进行插入查找。
map接口示例:
#include<iostream>
#include<map>
using namespace std;

void test01()
{
	pair<int, int>v1(1, 100);
	pair<int, int>v2(2, 200);
	pair<int, int>v3(3, 300);
	map<int, int>m1;

	m1.insert(v1);
	m1.insert(v2);
	m1.insert(v3);
}

void test02()
{
	pair<int, int>va[] = { {3,100},{6,600},{2,200},{7,700},{8,800} };
	map<int, int>m2; 
	int n = sizeof(va) / sizeof(va[0]);

	for (int i = 0; i < n; ++i)
		m2.insert(va[i]);//map 按照key值排序

	map<int, int>::iterator it = m2.begin();
	while (it != m2.end())
	{
		cout << it->first << " " << it->second << endl;
		it++;
	}
	cout << "---------------------" << endl;
	for (const auto& e : m2)
		cout << e.first << " " << e.second << endl;

	cout << "---------------------" << endl;
	for (int i = 0; i < m2.size(); ++i)//有的key没有value
	{
		cout << m2[i] << endl;
	}
	cout << "---------------------" << endl;
	m2[3] = 300;//修改value
	m2[1] = 100;//插入新的
	for (const auto& e : m2)
		cout << e.first << " " << e.second << endl;
	cout << "---------------------" << endl;
}

//vector的.at会检查下标越界,[]不检查

void test03()
{
    map<int, int>m3{ {1,100},{5,500},{3,300},{6,600} ,{2,200} };
	map<int, string>mp3{ {3,"abc"},{1,"xyz"},{5,"opq"},{4,"lmn"},{20,"hjk"} };
	
	mp3.insert(make_pair(8, "hym"));

	auto it=mp3.erase(mp3.begin());//返回下一个数据
	//auto  it = mp3.erase(5);//返回删除元素的个数
	//auto it = mp3.erase(mp3.begin(),mp3.end());//返回end()
	cout << it->first << " " << it->second << endl;

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

void test04()//迭代器失效
{
	map<int, string>mp4{ {3,"abc"},{1,"xyz"},{5,"opq"},{4,"lmn"},{20,"hjk"} };

	mp4.insert(make_pair(8, "hym"));
	auto pos = mp4.begin();
	pos=mp4.erase(pos);
	cout << pos->first << " " << pos->second << endl;
}

void test05()
{
	map<int, string>mp5{ {3,"abc"},{1,"xyz"},{5,"opq"},{4,"lmn"},{20,"hjk"} };

	mp5.insert(make_pair(8, "hym"));
	auto pos = mp5.find(50);//找到返回迭代器,找不到返回end
}

void test06()
{
	map<int, string>mp{ {3,"abc"},{1,"xyz"},{5,"opq"},{4,"lmn"},{20,"hjk"},{3,"lll"} };//key不允许重复值,value允许
	cout << mp.count(3) << endl;//为1

	multimap<int, string>mmp{ {3,"abc"},{1,"xyz"},{5,"opq"},{4,"lmn"},{20,"hjk"},{3,"lll"} };
	cout << mmp.count(3) << endl;//为2
}

void main()
{
	test06();
}
multimap:
1. Multimaps 是关联式容器,它按照特定的顺序,存储由 key value 映射成的键值对 <key, value> ,其中多个键值对之间的 key 是可以重复的。
2. multimap 中,通常按照 key 排序和惟一地标识元素,而映射的 value 存储与 key 关联的内容。 key 和value 的类型可能不同,通过 multimap 内部的成员类型 value_type 组合在一起, value_type 是组合 key value 的键值对 : typedef pair<const Key, T> value_type ;
3. 在内部, multimap 中的元素总是通过其内部比较对象,按照指定的特定严格弱排序标准对 key 进行排序的。
4. multimap 通过 key 访问单个元素的速度通常比 unordered_multimap 容器慢,但是使用迭代器直接遍历 multimap 中的元素可以得到关于 key 有序的序列。
5. multimap 在底层用二叉搜索树 ( 红黑树 ) 来实现。
       注意:multimap和map的唯一不同就是:map中的key是唯一的,而multimap中key是可以重复的。multimap中的接口可以参考map,功能都是类似的。
1. multimap中的key是可以重复的。
2. multimap中的元素默认将key按照小于来比较。
3. multimap中没有重载operator[]操作,不然存在二义性 !!!
4. 使用时与map包含的头文件相同。
multimap接口示例:
#include<iostream>
#include<functional>
#include<set>
#include<map>
using namespace std;
//重点:树形结构唯一会造成迭代器失效的情况就是删除一个迭代器对应的节点再对它操作
重点 key值一定是不能修改的,不然破坏树形结构
void test01()
{
	multimap<int, string>mmp{ {3,"abc"},{1,"xyz"},{5,"opq"},{4,"lmn"},{20,"hjk"},{3,"fff"} };

	mmp.insert(make_pair(8, "hym"));
	size_t count = mmp.erase(3);
	cout << count << endl;
}

void test02()
{
	multimap<int, string>mmp{ {3,"abc"},{1,"xyz"},{5,"opq"},{4,"lmn"},{20,"hjk"},{3,"fff"} };

	mmp.insert(make_pair(8, "hym"));
	auto pos = ++mmp.begin();
	auto it = mmp.erase(pos);
	cout << it->first << " " << it->second << endl;
}

void main()
{
	test02();
}

set容器:

1. set 是按照一定次序存储元素的容器
2. set 中,元素的 value 也标识它 (value 就是 key ,类型为 T) ,并且每个 value 必须是唯一的。 set 中的元素 不能在容器中修改 ( 元素总是 const) ,但是可以从容器中插入或删除它们。
3. 在内部, set 中的元素总是按照其内部比较对象 ( 类型比较 ) 所指示的特定严格弱排序准则进行排序。
4. set 容器通过 key 访问单个元素的速度通常比 unordered_set 容器慢,但它们允许根据顺序对子集进行直接迭代。
5. 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中查找某个元素,时间复杂度为: log2N
7. set中的元素不允许修改(为什么?) (修改会破坏底层树形结构)
8. set中的底层使用二叉搜索树(红黑树)来实现。

 T: set中存放元素的类型,实际在底层存储<value, value>的键值对。 Compareset中元素默认按照小于来比较 Allocset中元素空间的管理方式,使用STL提供的空间配置器管理。

#include<iostream>
#include<functional>
#include<set>
using namespace std;

class Test
{
public:
	Test(int a, int b) :m_a(a), m_b(b) {}
private:
	int m_a;
	int m_b;
};

void test01()
{
	set<int>s{ 5,6,3,2,8,9,1,0,4,7};
	set<int>::iterator it = s.begin();
	s.insert(6);//不能重复
	while (it != s.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
}

void test02()
{
	//set<int, string>s{ {1,"abc"},{2,"xyz"} };//不能这样写,不能俩类型参数
	//set<int, string>s;
	set<int,greater<int>>s{ 5,6,3,2,8,9,1,0,4,7 };
	set<int>::iterator it = s.begin();
	s.insert(6);//不能重复
	while (it != s.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
}

void test03()
{
	pair<int, string>v1{ 1,"xyz" };
	pair<int, string>v2{ 2,"abc" };
	set<pair<int, string>>s;
	s.insert(v1);
	s.insert(v2);

	for (const auto& e : s)
		cout << e.first << " " << e.second << endl;

	for (set<pair<int, string>>::iterator it = s.begin(); it != s.end(); it++)
	{
		cout << it->first << " " << it->second << endl;
	}
}

void test04()
{
	Test t1(0, 0);
	Test t2(1, 1);
	Test t3(2, 2);
	set<Test>s1;
}

void main()
{
	test03();
}

multiset:

1. multiset 是按照特定顺序存储元素的容器,其中元素是可以重复的。
2. multiset 中,元素的 value 也会识别它 ( 因为 multiset 中本身存储的就是 <value, value> 组成的键值对,因此 value 本身就是 key key 就是 value ,类型为 T). multiset 元素的值不能在容器中进行修改 ( 因为元素 总是 const ) ,但可以从容器中插入或删除。
3. 在内部, multiset 中的元素总是按照其内部比较规则 ( 类型比较 ) 所指示的特定严格弱排序准则进行排 序。
4. multiset 容器通过 key 访问单个元素的速度通常比 unordered_multiset 容器慢,但当使用迭代器遍历时会得到一个有序序列。
5. multiset 底层结构为二叉搜索树 ( 红黑树 )。
注意:
1. multiset中再底层中存储的是<value, value>的键值对
2. mtltiset的插入接口中只需要插入即可
3. 与set的区别是,multiset中的元素可以重复,set是中value是唯一的
4. 使用迭代器对multiset中的元素进行遍历,可以得到有序的序列
5. multiset中的元素不能修改
6. 在multiset中找某个元素,时间复杂度为 log2N
7. multiset的作用:可以对元素进行排序
#include<iostream>
#include<functional>
#include<set>
#include<map>
using namespace std;
//重点:树形结构唯一会造成迭代器失效的情况就是删除一个迭代器对应的节点再对它操作
重点 key值一定是不能修改的,不然破坏树形结构
void test01()
{
	multimap<int, string>mmp{ {3,"abc"},{1,"xyz"},{5,"opq"},{4,"lmn"},{20,"hjk"},{3,"fff"} };

	mmp.insert(make_pair(8, "hym"));
	size_t count = mmp.erase(3);
	cout << count << endl;
}

void test02()
{
	multimap<int, string>mmp{ {3,"abc"},{1,"xyz"},{5,"opq"},{4,"lmn"},{20,"hjk"},{3,"fff"} };

	mmp.insert(make_pair(8, "hym"));
	auto pos = ++mmp.begin();
	auto it = mmp.erase(pos);
	cout << it->first << " " << it->second << endl;
}

void main()
{
	test02();
}

        在C++98中,STL提供了底层为红黑树结构的一系列关联式容器,在查询时效率可达到 log2N
,即最差情况下需要比较红黑树的高度次,当树中的节点非常多时,查询效率也不理想。最好的查询是,进行很少的比较次 数就能够将元素找到,因此在C++11中,STL又提供了4个unordered系列的关联式容器,这四个容器与红黑 树结构的关联式容器使用方式基本类似,只是其底层结构不同。

unoredered_map、unordered_set、unordered_multimap及unordered_multiset底层为哈希表,unordered表示不排序,multi表示数据可以重复。

1. unordered_map是存储<key, value>键值对的关联式容器,其允许通过keys快速的索引到与其对应的value。
2. 在unordered_map中,键值通常用于惟一地标识元素,而映射值是一个对象,其内容与此键关联。键和映射值的类型可能不同。
3. 在内部,unordered_map没有对<kye, value>按照任何特定的顺序排序, 为了能在常数范围内找到key所对应的value,unordered_map将相同哈希值的键值对放在相同的桶中。
4. unordered_map容器通过key访问单个元素要比map快,但它通常在遍历元素子集的范围迭代方面效率较低。
5. unordered_maps实现了直接访问操作符(operator[]),它允许使用key作为参数直接访问value。
6. 它的迭代器至少是前向迭代器。

 

 

#include<iostream>
#include<unordered_map>
using namespace std;

//unordered_map又叫散列
//不排序(底层为hash表),不重复
void test01()
{
	unordered_map<int, string>mmp{ {3,"abc"},{1,"xyz"},{5,"opq"},{4,"lmn"},{20,"hjk"},{3,"fff"} };

	mmp.insert(make_pair(8, "hym"));
	for (const auto& e : mmp)
	{
		cout << e.first << " " << e.second << endl;
	}
}

//不排序但是允许重复
void test02()
{
	unordered_multimap<int, string>mmp{ {3,"abc"},{1,"xyz"},{5,"opq"},{4,"lmn"},{20,"hjk"},{3,"fff"} };

	mmp.insert(make_pair(8, "hym"));
	for (const auto& e : mmp)
	{
		cout << e.first << " " << e.second << endl;
	}
}

void main()
{
	test02();
}

unordered_set可以查阅官方文档(cplusplus.com)进行学习

#include<iostream>
#include<functional>
#include<unordered_set>
using namespace std;

//unoredred_set不排序,不重复
void test01()
{
	unordered_set<int>s{ 5,6,3,2,8,9,1,0,4,7,1,5,8};
	unordered_set<int>::iterator it = s.begin();
	s.insert(6);//不能重复
	while (it != s.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
}

//允许重复不排序
void test02()
{
	unordered_multiset<int>s{ 5,6,3,2,8,9,1,0,4,7,1,5,8 };
	unordered_multiset<int>::iterator it = s.begin();
	s.insert(6);
	while (it != s.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
}

void main()
{
	test02();
}

        hash_map和unordered_map,hash_set和unordered_set,可能具体的哈希表实现不同,但是几乎等价。

#define  _SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS
#include<iostream>
#include<unordered_map>
#include<hash_map> 
using namespace std;

//unordered_map又叫散列 等价于hash_map 
//不排序(底层为hash表),不重复
void test01()
{
	hash_map<int, string>mmp{ {3,"abc"},{1,"xyz"},{5,"opq"},{4,"lmn"},{20,"hjk"},{3,"fff"} };

	mmp.insert(make_pair(8, "hym"));
	for (const auto& e : mmp)
	{
		cout << e.first << " " << e.second << endl;
	}
}

//不排序但是允许重复 等价于 hash_multimap
void test02()
{
	unordered_multimap<int, string>mmp{ {3,"abc"},{1,"xyz"},{5,"opq"},{4,"lmn"},{20,"hjk"},{3,"fff"} };

	mmp.insert(make_pair(8, "hym"));
	for (const auto& e : mmp)
	{
		cout << e.first << " " << e.second << endl;
	}
}

void main()
{
	test01();
}
#define _SILENCE_STDEXT_HASH_DEPRECATION_WARNINGS
#include<iostream>
#include<functional>
#include<unordered_set>
#include<hash_set>
using namespace std;

//unoredred_set不排序,不重复 等价于 hash_set
void test01()
{
	hash_set<int>s{ 5,6,3,2,8,9,1,0,4,7,1,5,8 };
	hash_set<int>::iterator it = s.begin();
	s.insert(6);//不能重复
	while (it != s.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
}

//允许重复不排序 等价于 hash_multiset
void test02()
{
	hash_multiset<int>s{ 5,6,3,2,8,9,1,0,4,7,1,5,8 };
	hash_multiset<int>::iterator it = s.begin();
	s.insert(6);
	while (it != s.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;
}

void main()
{
	test01();
}


 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值