Set和Map

一、Set的介绍

1.1、Set相关文档介绍

cplusplus.com/reference/set/set/?kw=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中存储的是真正的键值对,set中只放
value,但在底层实际存放的是由构成的键值对。
2. set中插入元素时,只需要插入value即可,不需要构造键值对。
3. set中的元素不可以重复(因此可以使用set进行去重)。
4. 使用set的迭代器遍历set中的元素,可以得到有序序列
5. set中的元素默认按照小于来比较(升序)
6. set中查找某个元素,时间复杂度为:log2 
7. set中的元素不允许修改
8. set中的底层使用二叉搜索树(红黑树)来实现

1.2、Set的使用

既然set是容器,那他也可以像前几个一样被构造。头文件#include

1.空构造和拷贝构造
        set s1;
        set s2(s1);
2.迭代器构造
        string s3("I love C++");
        set s4(s.begin(), s.end());
3.大于比较
        set的底层是搜素二叉树,默认是小于排序(升序),也可是调成大于排序(降序)。

        set> s5;

迭代器接口:

Set和Map_第1张图片

容量接口:
Set和Map_第2张图片

修改接口:

Set和Map_第3张图片

这里我们对Set进行一些测试:

#include
#include
using namespace std;
 
void test1()
{
	set s1;
	s1.insert(1);   //插入操作
	s1.insert(3);
	s1.insert(5);
	s1.insert(7);
	s1.insert(4);
    s1.insert(4);
    s1.insert(5);
    s1.insert(1);
	set s2(s1);
	set::iterator it = s1.begin();
	for (auto e : s1)
	{
		cout << e << " ";   //  1 3 4 5 7,是升序排列,别搞错了
	}
	cout << endl;
	set::iterator pos1 = find(s1.begin(), s1.end(), 5);
	set::iterator pos2 = s1.find(7);   //set自带的查找函数
	if (it != s1.end())
	{
		s1.erase(pos1);   //删除5
		s1.erase(pos2);
		s1.insert(10);
		s1.insert(20);
	}
	while (it != s1.end())
	{
		cout << *it << " ";    //1 3 4 10 20
		*it++;
	}
	cout << endl;
	//删除指定区间
	set::iterator ret = s2.find(5);
	s2.erase(s2.begin(), ret);
	for (auto e : s2)
	{
		cout << e << " ";   //5 7
	}
	cout << endl;
  //寻找是否有val,找到返回1
	if (s2.count(5)) // s2中有5就返回1,进入循环
	{
		cout << "s2中有5:" << endl;  //s2中有5
	}
	cout << s2.empty() << endl;  // 判空
	cout << s1.size() << endl;   //s1的个数是5
	cout << s2.size() << endl;   //s2的个数是2
}
 
int main()
{
	test1();
}

二、multiset 的介绍

2.1、multiset相关文档介绍

cplusplus.com/reference/set/multiset/?kw=multiset

1. multiset是按照特定顺序存储元素的容器,其中元素是 可以重复 的。
2. 在multiset中,元素的value也会识别它(因为multiset中本身存储的就是组成
的键值对,因此value本身就是key,key就是value,类型为T). multiset元素的值不能在容器
中进行修改(因为元素总是const的),但可以从容器中插入或删除。
3. 在内部,multiset中的元素总是按照其内部比较规则(类型比较)所指示的特定严格弱排序准则
进行排序。
4. multiset容器通过key访问单个元素的速度通常比unordered_multiset容器慢,但当使用迭
代器遍历时会得到一个有序序列。
5. multiset底层结构为二叉搜索树(红黑树)。

注意:

1. multiset中再底层中存储的是的键值对
2. mtltiset的插入接口中只需要插入即可
3. 与set的区别是,multiset中的元素可以重复,set是中value是唯一的
4. 使用迭代器对multiset中的元素进行遍历,可以得到有序的序列
5. multiset中的元素不能修改
6. 在multiset中找某个元素,时间复杂度为O(log2)
7. multiset的作用:可以对元素进行排序

2.2、multiset的使用

 multiset的count和erase跟set相比有很大区别:

1、set.count(val)是如果找到val就返回1;因为multiset的数可以重复,multiset.count(val)找到val后返回multiset中有几个val。
2、set.erase(val)是找到要删除的val后,返回1;multiset.erase(val)找到要删除的val后返回val有几个,并且全删val。

这里我们对multiset进行一些测试:

void test2()
{
	multiset multiset;
	multiset.insert(4);
	multiset.insert(5);
	multiset.insert(2);
	multiset.insert(1);
	multiset.insert(1);
	multiset.insert(3);
	multiset.insert(3);
	multiset.insert(3);
	for (auto e : multiset)
	{
		cout << e << " ";   
	}
	cout << endl;
	set set(multiset.begin(), multiset.end());
	
 
	cout << multiset.erase(1) << endl;//2 返回删除的1的个数
	for (auto e : multiset)
	{
		cout << e << " ";  //2 3 3 3 4 5
	}
	cout << endl;
	cout << set.erase(4) << endl;//1 返回一个bool值,存在删除的值返回1
	for (auto e : multiset)
	{
		cout << e << " ";  //2 3 3 3 4 5
	}
	cout << endl;
	auto pos1 = multiset.find(3); //返回中序的第一个3的迭代器
	while (pos1 != multiset.end())
	{
		cout << *pos1 << " ";//3 3 3 4 5
		pos1++;
	}
}

三、Map的介绍

3.1、Map相关文档介绍

cplusplus.com/reference/map/map/?kw=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通常被实现为二叉搜索树(更准确的说:平衡二叉搜索树(红黑树 ))

3.2、Map的使用

Set和Map_第4张图片

#include
#include
using namespace std;
 
int main()
{
    map m;//创建一个map对象m
	//向m中插入pair
 
	map m1(m.begin(), m.end);//用m的区间构造m1
	map m2(m1);//用m1拷贝构造m2
 
	return 0;
}
key: 键值对中key的类型
T: 键值对中value的类型
Compare: 比较器的类型,map中的元素是按照key来比较的,缺省情况下按照小于来比
较,一般情况下(内置类型元素)该参数不需要传递,如果无法比较时(自定义类型),需要用户
自己显式传递比较规则(一般情况下按照函数指针或者仿函数来传递)
Alloc:通过空间配置器来申请底层空间,不需要用户传递,除非用户不想使用标准库提供的
空间配置器
注意:在使用map时,需要包含头文件#include
map的迭代器
Set和Map_第5张图片
map的容量与元素访问
        
Set和Map_第6张图片
注意:在元素访问时,有一个与operator[ ]类似的操作at()(该函数不常用)函数,都是通过
key找到与key对应的value然后返回其引用,不同的是:当key不存在时,operator[ ]用默认
value与key构造键值对然后插入,返回该默认value,at()函数直接抛异常。
map中元素的修改
map的key不允许被修改。
Set和Map_第7张图片
map中元素的查找
map可以用[ ]进行访问(不是下标访问,一定要注意)
mapped_type& operator[] (const key_type& k);

具体实现如下:

mapped_type& operator[] (const key_type& k)
{
	return (*((this->insert(make_pair(k, mapped_type()))).first)).second;
}

这个如果不好理解的话,看一下下面这个:

mapped_type& operator[] (const key_type& k)
{
    //1、调用insert返回迭代器区间
	pair ret = insert(make_pair(k, mapped_type()));
    //2、利用ret调用value值
	return ret.first->second;//return (*(ret.first)).second;
}
  • 首先调用insert函数插入键值对返回迭代器ret
  • 通过返回的迭代器ret调用元素值value。

在这里我们又分两种情况去处理:

1、如果k在map对象中,则插入失败,返回的bool类型为false,返回的迭代器为k所在结点的迭代器,而迭代器解引用*(ret.first)获得的就是pair,最后再通过pair访问到value值,整体可优化成ret.first->second,这里返回引用的好处为查找k对应v,修改k对应v。


2、如果k不在map对象中,则插入成功,返回的bool类型为true,返回的迭代器为新插入的k所在结点的迭代器位置,接着调用ret.first获得pair的迭代器,再通过->second获得value,这里返回引用的好处为插入和修改。

对Map的测试:
void test_map1()
{
	map dict;
	//dict.insert(pair("sort", "排序"));
	dict.insert(make_pair("sort", "排序"));
	dict.insert(make_pair("string", "字符串"));
	dict.insert(make_pair("count", "计数"));

	//map::iterator dit = dict.begin();
	auto dit = dict.begin();
	while (dit != dict.end())
	{
		cout << (*dit).first << ":" << (*dit).second << endl;
		++dit;
	}
	cout << endl;
}

void test_map2()
{
	map dict;
	//dict.insert(pair("sort", "排序"));
	dict.insert(make_pair("sort", "排序"));
	dict.insert(make_pair("string", "字符串"));
	dict.insert(make_pair("count", "计数"));
	dict.insert(make_pair("string", "(字符串)")); // 插入失败

	dict["left"];			// 插入
	dict["right"] = "右边"; // 插入+修改
	dict["string"] = "(字符串)"; // 修改
	cout << dict["string"] << endl; // 查找
	cout << dict["string"] << endl; // 查找


	//map::iterator dit = dict.begin();
	auto dit = dict.begin();
	while (dit != dict.end())
	{
		//cout << (*dit).first << ":" << (*dit).second << endl;
		cout << dit->first << ":" << dit->second << endl;

		++dit;
	}
	cout << endl;
}

void test_map3()
{
	string arr[] = { "西瓜", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉", "梨" };
	map countMap;
	//for (auto& e : arr)
	//{
	//	auto ret = countMap.find(e);
	//	if (ret == countMap.end())
	//	{
	//		countMap.insert(make_pair(e, 1));
	//	}
	//	else
	//	{
	//		ret->second++;
	//	}
	//}

	for (auto& e : arr)
	{
		countMap[e]++;
	}

	for (auto& kv : countMap)
	{
		cout << kv.first << ":" << kv.second << endl;
	}
}

//V& operator[](const K& key)
//{
//	pair ret = insert(make_pair(key, V()));
//	return ret.first->second;
//}

int main()
{
	test_map1();
    test_map2();
    test_map3();
	return 0;
}
【总结】
1. map中的的元素是键值对
2. map中的key是唯一的,并且不能修改
3. 默认按照小于的方式对key进行比较
4. map中的元素如果用迭代器去遍历,可以得到一个有序的序列
5. map的底层为平衡搜索树(红黑树),查找效率比较高O(log2)
6. 支持[]操作符,operator[]中实际进行插入查找。

四、multiset的介绍

4.1、multiset相关文档介绍

1. Multimaps是关联式容器,它按照特定的顺序,存储由key和value映射成的键值对
value>,其中多个键值对之间的key是可以重复的。
2. 在multimap中,通常按照key排序和惟一地标识元素,而映射的value存储与key关联的内
容。key和value的类型可能不同,通过multimap内部的成员类型value_type组合在一起,
value_type是组合key和value的键值对: typedef pair value_type;
3. 在内部,multimap中的元素总是通过其内部比较对象,按照指定的特定严格弱排序标准对
key进行排序的。
4. multimap通过key访问单个元素的速度通常比unordered_multimap容器慢,但是使用迭代
器直接遍历multimap中的元素可以得到关于key有序的序列。
5. multimap在底层用二叉搜索树(红黑树)来实现。
注意: multimap和map的唯一不同就是:map中的key是唯一的,而multimap中key是可以
重复的

4.2、multimap的使用

1、map中的valuevalue是唯一的,不允许重复。multimap可以。

2、multimap中没有[ ]。因为它的key和value是一对多的关系,比如上面的苹果,那key是苹果,但是苹果出现了好几次,每一个都有一个value,那他到底返回哪一个呢?为啥map能返回呢,因为在map中每一个key(苹果)都对应一个自己的value,所以每次返回一个value.但是multimap是一个key有好几个value.

multimap测试:
void test7()
{
	multimap mlp1;
	mlp1.insert(make_pair("one", "一"));
	mlp1.insert(make_pair("two", "二"));
	mlp1.insert(make_pair("three", "三"));
	mlp1.insert(make_pair("four", "四"));
    mlp1.insert(make_pair("four", "四"));
	mlp1.insert(make_pair("spring", "五"));
    mlp1.insert(make_pair("spring", "五"));
 
	for (auto e : mlp1)
	{
		cout << e.first << ":" << e.second << endl;
	}
}
int main()
{
	test7();
}

你可能感兴趣的:(算法,开发语言,c++,数据结构)