轻松搞懂map/set

一.引入

之前学的vector、list、deque、forward_list(C++11)等,这些容器统称为序列式容器,因为其底层为线性序列的数据结构,里面存储的是元素本身。关联式容器也是用来存储数据的,与序列式容器不同的是,其里面存储的是<key, value>结构的键值对,在数据检索时比序列式容器效率更高。

什么又是键值对呢?

用来表示具有一一对应关系的一种结构,该结构中一般只包含两个成员变量key和value,key代表键值,value表示与key对应的信息。

SGI-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)
        {}
};

 根据应用场景的不同,STL总共实现了两种不同结构的管理式容器:树型结构哈希结构。树型结
构的关联式容器主要有四种:map、set、multimap、multiset。这四种容器的共同点是:使
平衡搜索树(即红黑树)作为其底层结果,容器中的元素是一个有序的序列。

二.set

一.set的介绍与注意点

cplusplus.com/reference/set/set/?kw=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中查找某个元素,时间复杂度为:logN

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

二.set常用/重点接口的使用

template<class _Ty = void>
struct more
	: public binary_function<_Ty, _Ty, bool>
{	// functor for operator<
	bool operator()(const _Ty& _Left, const _Ty& _Right) const
	{	// apply operator< to operands
		return (_Left > _Right);
	}
};

void test_set1()
{
	set<int> st;//set中只存value,但在底层实际存放的是由<value, value>构成的键值对
	/*set<int,more<int>> st;*///仿函数,来实现排降序
	st.insert(3);
	st.insert(5);
	st.insert(8);
	st.insert(7);
	//迭代器
	 set<int>::iterator it = st.begin();
	//set<int>::reverse_iterator it = st.rbegin();
	while (it != st.end())
	{
		cout << *it << " ";//3 5 7 8  ---->默认时升序排的 
		it++;
	}
	cout << endl;
	//capacity
	cout << st.empty() << endl;
	cout << st.size() << endl;
	cout << st.max_size() << endl;//返回 set 容器可以容纳的最多元素个数。
	//modified
	st.insert(0);//在set中插入元素x,实际插入的是<x, x>构成的键值对,如果插入成功,返回<该元素在set中的位置(迭代器/指针),true>
	//如果插入失败,说明x在set中已经存在,返回<x在set中的位置(迭代器/指针),false>
	st.insert(99);
	cout << *st.insert(99).first << endl;//99
	cout << st.insert(99).second << endl;//false

	st.erase(99);
	st.erase(st.find(0));
	for (auto e : st)
	{
		cout << e << " ";//3 5 7 8 
	}
	cout << endl;
	auto first = st.find(5);
	auto last = st.find(8);
	st.erase(first, last);//删除set中[first, last)区间中的元素
	for (auto e : st)
	{
		cout << e << " ";//3 8
	}
	cout << endl;
	
	cout << st.count(3) << endl;//返回某个元素的个数

}

三.map

一.map的介绍与注意点

map - C++ Reference (cplusplus.com)

1. map是关联容器,它按照特定的次序(按照key来比较)存储由键值key和值value组合而成的元
素。
2. 在map中,键值key通常用于排序和惟一地标识元素,而值value中存储与此键值key关联的
内容。键值key和值value的类型可能不同,并且在map的内部,key与value通过成员类型
value_type绑定在一起,为其取别名称为pair:typedef pair<const key, T> value_type;

3. 在内部,map中的元素总是按照键值key进行比较排序的,默认是小于的方式比较。
4. map中通过键值访问单个元素的速度通常比unordered_map容器慢,但map允许根据顺序
对元素进行直接迭代(即对map中的元素进行迭代时,可以得到一个有序的序列)。
5. map支持下标访问符,即在[]中放入key,就可以找到与key对应的value。
6. map通常被实现为二叉搜索树(更准确的说:平衡二叉搜索树(红黑树))。

7.map的底层为平衡搜索树(红黑树),查找效率比较高O(log_2 N)

二.map常用/重点接口的使用

    map<string, string> dict;
如何插入
	pair<string, string> kv1("insert", "插入");
	dict.insert(kv1);//map插入的是一个pair的结构体对象
	dict.insert(pair<string, string>("sort", "排序"));//匿名对象
	// C++98
	dict.insert(make_pair("string", "字符串"));//较为常用
	// C++11 多参数的构造函数隐式类型转换
	dict.insert({ "string", "字符串" });
//insert返回值的探究
//插入过程中,只比较key,value是相同无所谓
	/*pair<iterator, bool> insert(const value_type & x)<==>pair< map<string,int>::iterator , bool >!!!!!!!!!!!!!重要
		在map中插入键值对x,注意x是一个键值
		对,返回值也是键值对:iterator代表新插入
		元素的位置,bool代表是否插入成功*/
	//若插入成功iterator,就指向新插入的数据。
	//若插入不成功,就指向与待插元素冲突的数据。
	auto p=dict.insert(make_pair("insert", "xxxx"));
	cout <<p.first->first << endl;
	//cout << (*p.first).first << endl;//还可以这样写
	cout << p.first->second << endl;
	cout << p.second << endl;//bool
	

	cout << dict.count("insert") << endl;
	//返回key为x的键值在map中的个数,注意map中key是唯一的,因此该函数的返回值要么为0,要么为1,因此也可以用该函数来检测一个key是否在map中

	for (auto it = dict.begin(); it !=dict.end(); it++)
	{
		//cout << it->first << ":" << it->second << endl;
		cout<<(*it).first << ":" << (*it).second << endl;
	}
	cout << "---------------------------------------" << endl;
	dict.erase("insert");
	dict.erase(dict.find("sort"));
	//也可以用删除[first, last)区间的办法删除部分元素
	for (auto it = dict.begin(); it != dict.end(); it++)
	{
		//cout << it->first << ":" << it->second << endl;
		cout << (*it).first << ":" << (*it).second << endl;
	}
	cout << "---------------------------------------" << endl;
	dict.insert(make_pair("apple", "苹果"));
	dict.insert(make_pair("banana", "香蕉"));
///map的下标操作///很重要
	//map支持[]操作:mapped_type& operator[] (constkey_type& k) ---返回去key对应的value
	cout << dict["apple"] << endl;//苹果
	//当key不在map中时,通过operator获取对应value时会发生什么问题?
	cout << dict["bear"] << endl;//空
	//回答这个问题,我们先要知道operator[]是如何实现的
	//实现过程:(*((this->insert(make_pair(k, mapped_type()))).first)).second
	//步骤拆解:1.make_pair(k, mapped_type()//使用k作为关键字,并创建了匿名对象作为值,构成了键值对
	//			2.this->insert(make_pair(k, mapped_type()))//将构建的键值对插入到对象当中
	//          3.(this->insert(make_pair(k,mapped_type()))).first//找到构建的键值对在对象的迭代器
	//          4.*((this->insert(make_pair(k,mapped_type()))).first)//解引用,找到那个键值对
	//			5.(*((this->insert(make_pair(k,mapped_type()))).first)).second//获取键值对里面的值(value)
	dict["bear"] = "梨";
	dict["set"] = "集合";//故我们可以通过这样的方式来添加。插入+修改
	for (auto it = dict.begin(); it != dict.end(); it++)
	{
		cout << it->first << ":" << it->second << endl;
	}

总结:

在set/与map的接口使用的部分没有全部写完,大部分接口与我们之前学的容器的接口功能类似,也有部分接口不咋经常使用。set的接口我认为需要重点看看的是为insert()和count();map的是insert(),operator[]和count()。

  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值