【C++】map与set

  人虽然可以为所欲为,但却不能得偿所愿。 

前言 

  这是我自己学习C++的第十四篇博客总结。后期我会继续把C++学习笔记开源至博客上。

  上一期笔记是关于C++的搜索二叉树知识,没看的同学可以过去看看:

【C++】二叉搜索树-CSDN博客https://blog.csdn.net/hsy1603914691/article/details/146887483?spm=1001.2014.3001.5502

序列式容器与关联式容器

1. stringvectorstackqueuedeque等都属于是序列式容器逻辑结构为线性序列,任意两个位置的储存值之间没有紧密的关联关系

2. 序列式容器保存和访问储存值时,一般依靠储存值在容器中储存的位置

3. set、map、unordered_set、unordered_map都属于是关联式容器逻辑结构为非线性结构,任意两个位置的储存值之间存在紧密的关联关系

4. 关联式容器保存和访问储存值时,一般依靠容器中的关键字Key

set类

set类的介绍 

1. 在使用set类时,必须包含 #include <set> 这一行。

2. set类的底层其实是一个红黑树结构,使用时需要显示实例化

3. 下面是set类的官方文本介绍,里面有详细的用法讲解。

- C++ Reference https://legacy.cplusplus.com/reference/set/

set类对象的常见构造

1. set<int> s1,什么也不需要传入,构造一个空的set类对象

2. set<int> s2(s1.begin(),s1.end()),使用另一个set类对象进行迭代器构造

3. set<int> s3(const set<int>& s2),使用另一个set类对象进行拷贝构造

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <set>
using namespace std;

int main()
{
	set<int> s1;
	set<int> s2(s1.begin(), s1.end());
	set<int> s3(const set<int>& s2);
	return 0;
}

set类对象的容量操作  

1. set.size(),返回set类对象有效元素个数

2. set.empty(),检测set类对象有效节点是否为空为空返回true不为空返回flase

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <set>
using namespace std;

int main()
{
	set<int> s1;
	s1.insert(1);
	s1.insert(4);
	s1.insert(3);
	s1.insert(2);
	s1.insert(5);
	s1.insert(6);
	cout << s1.size() << endl;//6
	cout << s1.empty() << endl;//0
	return 0;
}

set容器的修改操作

1. set.insert(int num)向set类对象中插入整数num如果插入set类对象已有的元素则插入失败

2. set.erase(int num)向set类对象中删除整数num如果删除set类对象没有的元素则删除失败

3. set.erase(iterator pos)向set类对象中删除迭代器为pos的值

4. set.find(int num)检查set类对象中是否存在某个特定的元素num效率为log(N)如果找到了则返回指向元素num的迭代器如果没有找到则返回指向end()的迭代器

5. set.count(int num)检查set类对象中是否存在某个特定的元素num返回元素num的个数

6. set.lower_bound(int num)返回一个迭代器指向set类对象中第一个大于等于num的元素

7. set.upper_bound(int num)返回一个迭代器指向set类对象中第一个大于num的元素

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

int main()
{
	set<int> s1;
	s1.insert(1);
	s1.insert(4);
	s1.insert(3);
	s1.insert(2);
	s1.insert(5);
	s1.insert(6);
	s1.insert(6);//如果插入set对象中已有元素,则插入失败
	for (auto it : s1)
	{
		cout << it << " ";//123456
	}
	cout << endl;
	s1.erase(6);
	s1.erase(7);//如果删除set对象中未有元素,则删除失败
	for (auto it : s1)
	{
		cout << it << " ";//12345
	}
	cout << endl;
	s1.erase(s1.begin());
	for (auto it : s1)
	{
		cout << it << " ";//2345
	}
	cout << endl;
	auto it = s1.find(4);
	cout << *it << endl;//4
	if (s1.count(3))
	{
		cout << "存在3";//"存在3"
	}
	return 0;
}

set类对象的遍历操作 

1. set类支持迭代器操作迭代器遍历走二叉搜索树的中序遍历因此遍历默认为升序

2. set.begin()set.end()set.begin()获取第一个元素的迭代器,set.end()获取最后一个元素的下一个位置的迭代器。

3. set.rbegin()set.rend()set.rbegin()获取最后一个元素的迭代器,set.rend()获取第一个元素的上一个位置的迭代器。

//insert()
#include <iostream>
#include <set>
using namespace std;

int main()
{
	set<int> s1;
	s1.insert(1);
	s1.insert(4);
	s1.insert(3);
	s1.insert(2);
	s1.insert(5);
	s1.insert(6);
	s1.insert(6);//如果插入set对象中已有元素,则插入失败
    auto it=s1.begin();
    while(it!=s1.end())
    {
        cout << *it << " ";
        it++;
    }
	return 0;
}
//1 2 3 4 5 6

multiset类

1. multisetset的使用基本类似,主要区别点在于multiset支持值冗余,那么insertfindcounterase都围绕着支持冗余有所差异。

2. insert 插入 multiset类对象 已有元素时, 不会插入失败 而会正常插入
3.  find 查找 multiset类对象 已有元素时, 若存在多个 则返回第一个元素的迭代器
4.  erase 删除 multiset类对象 已有元素时, 若存在多个 删除全部的该元素
5.  count 查找 multiset类对象 已有元素时, 若存在多个 则会返回该元素的全部个数
#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <set>
using namespace std;

int main()
{
	multiset<int> m1;
	m1.insert(1);
	m1.insert(1);
	m1.insert(2);
	m1.insert(3);
	m1.insert(3);
	m1.insert(4);
	m1.insert(5);
	m1.insert(6);
	m1.insert(2);
	m1.insert(4);
	for (auto e : m1)
	{
		cout << e << " ";//1122334456
	}
	cout << endl;
	auto it = m1.find(3);
	cout << *it << endl;//3
	cout << m1.count(1) << endl;//2
	m1.erase(2);
	for (auto e : m1)
	{
		cout << e << " ";//11334456
	}
	cout << endl;
	return 0;
}

map类

map类的介绍 

1. 在使用map类时,必须包含 #include <map> 这一行。

2. map类的底层其实是一个红黑树结构,使用时需要显示实例化

3. 下面是map类的官方文本介绍,里面有详细的用法讲解。

- C++ Referencehttps://legacy.cplusplus.com/reference/map/

pair类型的介绍 

1. map的每个节点都存储了一个键值对,键值对的数据类型为 pair < Key , Value >

2. 键值对需要使用大括号来包裹:{ Key , Value },如果是键值对的集合,则需另外用一层大括号来包裹:{ { Key1 , Value1 } , { Key2 , Value2 } , { Key3 , Value3 } }

template <class T1,class T2>
struct pair
{
	T1 first;
	T2 second;
	pair()
		:first(T1())
		,second(T2())
	{}
};

map类对象的常见构造

1. map<string,string> s1,什么也不需要传入,构造一个空的map类对象

2. map<string,string> s2(s1.begin(),s1.end()),使用另一个map类对象进行迭代器构造

3. map<string,string> s3(const map<string,string>& s2),使用另一个map类对象进行拷贝构造

#define _CRT_SECURE_NO_WARNINGS
#include <iostream>
#include <map>
using namespace std;

int main()
{
	map<string, string> m1;
	map<string, string> m2(m1.begin(),m1.end());
	map<string, string> m3(m2);
	return 0;
}

1. map类对象的初始化分为两种。 

2. 如果使用号,则为拷贝初始化;如果不使用号,则为直接初始化。 

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

int main()
{
	map<string, string> m1 = { { "1", "难绷" },{"2","呵呵"} };
	map<int, string> m2{ { 1, "难绷" } ,{2,"呵呵"} };
}

map容器的修改操作 

1. map.insert({key,value})向map类对象中插入键值对如果插入map类对象已有的键key则插入失败

2. map.find(key)检查map类对象中是否存在某个特定的键key效率为log(N)返回一个迭代器如果找到了则返回指向键key的迭代器如果没有找到则返回指向end()的迭代器

3. map.count(key)检查map类对象中是否存在某个特定的键key返回键key的个数

4. map.erase(key)map类对象中删除键为key的键值对如果删除map类对象没有键key则删除失败

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

int main()
{
	map<string, string> m1{ { "right","右边" } };
	m1.insert({ "left","左边" });
	m1.insert({ "left","2边" });//向map类对象中插入已有的键,插入失败
	m1.insert({ "front","前面" });
	m1.insert({ "back","后面" });
	if (m1.count("front"))
	{
		auto it = m1.find("front");
		cout << it->first << " : " << it->second << endl;//front : 前面
	}
	m1.erase("front");
	m1.erase({ "hehe","2边" });//向map类对象中删除未有的键,删除失败
	if (m1.count("front"))
	{
		auto it = m1.find("front");
		cout << it->first << " : " << it->second << endl;
	}
	else
	{
		cout << "不存在front" << endl;//不存在front
	}
	return 0;
}

map类对象的遍历操作 

1. map类支持迭代器操作迭代器遍历走二叉搜索树的中序遍历因此默认为升序

2. map.begin()map.end()map.begin()获取第一个元素的迭代器,map.end()获取最后一个元素的下一个位置的迭代器。

3. map.rbegin()map.rend()map.rbegin()获取最后一个元素的迭代器,map.rend()获取第一个元素的上一个位置的迭代器。

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

int main()
{
	map<string, string> m1 = { {"1","难绷"},{"2","难说"},{"3","难道"} };
	auto it = m1.begin();
	while (it != m1.end())
	{
		cout << it->first << " ";
		it++;
	}
	cout << endl;
	for (auto e : m1)//e的类型和m1相同
	{
		cout << e.second << " ";
	}
	return 0;
}

//1 2 3
//难绷 难说 难道

map类对象的数据修改

1. map支持修改value值不支持修改key键,如果修改关键字数据,就破坏了底层搜索树的结构。

2. map 还有一个非常重要的接口 [ ] ,但是  [ ]  不仅仅支持修改,还支持插入数据和查找数据,它是一个多功能复合接口。
3.  insert() 插入一个 pair<key,value>键值对
  • 如果键key已经在map中,则插入失败,返回一个pair<iterator,bool>键值对first是键key所在结点的迭代器second是false
  • 如果键key不在map中,则插入成功,则返回一个pair<iterator,bool>对象first是新插入的键key所在结点的迭代器second是true
  • 无论插入成功还是失败,返回的pair<iterator,bool>键值对的first都会指向键key所在的迭代器
  • 也就意味着insert插入失败时也充当了查找的功能,正是因为这一点,insert可以用来实现 [ ]
  • 需要注意的是这里有两个pair,一个是map底层红黑树节点中储存的pair<key,Value>,另一个是insert返回的pair<iterator,bool>
mapped_type& operator[] (const key_type& k)
{
    pair<iterator, bool> ret = insert({ k, mapped_type() });
    iterator it = ret.first;
    return it->second;
}
//[]的各种功能
#include <iostream>
#include <map>
using namespace std;

int main()
{
	map<string, string> m1{ {"apple","苹果"} };
	m1["pear"] = "梨子";//如果不存在键key,则[]可以实现插入
	m1["left"] = "左边";
	m1["banana"] = "香蕉";
	m1["left"] = "左边、剩下";//如果已经存在键key,则[]可以实现修改和查找
	for (auto e : m1)
	{
		cout << e.second << endl;
	}
	return 0;
}
//不用[]统计数量
#include <iostream>
#include <map>
using namespace std;

int main()
{
	string arr[] = { "苹果","香蕉","西瓜","橘子","葡萄","苹果","苹果","橘子","苹果" };
	map<string, int> m1;
	for (auto e : arr)
	{
		auto it = m1.find(e);
		if (it == m1.end())
		{
			m1.insert({e,1});
		}
		else
		{
			it->second++;
		}
	}
	for (auto e : m1)
	{
		cout << e.first << " -> " << e.second << endl;
	}
	return 0;
}
//使用[]统计数量
#include <iostream>
#include <map>
using namespace std;

int main()
{
	string arr[] = { "苹果","香蕉","西瓜","橘子","葡萄","苹果","苹果","橘子","苹果" };
	map<string, int> m1;
	for (auto e : arr)
	{
		m1[e]++;
	}
	for (auto e : m1)
	{
		cout << e.first << " -> " << e.second << endl;
	}
	return 0;
}

multimap类

1. multimap map 使用 基本类似 ,主要区别点在于 multimap支持键key冗余 ,那么 insert find count erase 都围绕着 支持键key冗余 有所差异。
2. 其次就是 multimap不支持 [ ] ,因为 multimap支持键key冗余 [ ]  就只能实现插入了,不能支持修改,此时 [ ] 实现的意义并不大

致谢

  感谢您花时间阅读这篇文章!如果您对本文有任何疑问、建议或是想要分享您的看法,请不要犹豫,在评论区留下您的宝贵意见。每一次互动都是我前进的动力,您的支持是我最大的鼓励。期待与您的交流,让我们共同成长,探索技术世界的无限可能! 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值