STL详解---set和map

目录

1.set

(1)概念

(2)遍历

迭代器法遍历

范围for遍历

(3)插入与删除

insert()

erase()

(4)multiset

(5)set总结

2.map

(1)概念

(2)插入

insert的使用

insert的返回值

[]的重载使用

(3)遍历

迭代器遍历

范围for遍历

(4)删除

(5)multimap


1.set

(1)概念

set本质上是搜索二叉树的key模型。set的底层依然使用到了模板,因此我们这样定义set类型的对象:

set<int> s;

(2)遍历

迭代器法遍历
	set<int>::iterator it = s.begin();//定义迭代器it为s.begin()
	while (it != s.end())//依次遍历s中的数据
	{
		cout << *it << " ";
		it++;
	}
范围for遍历
	for (auto& e: s)
	{
		cout << e << " ";
	}

(3)插入与删除

insert()

使用insert()函数进行插入数据,具有去重的作用,这是因为set的底层就是一个key模型的搜索二叉树。是没有相同的元素的。

	s.insert(3);
	s.insert(5);
	s.insert(1);
	s.insert(1);
	s.insert(7);
	s.insert(0);
	s.insert(6);
	s.insert(9);
	s.insert(9);

当向s中插入这一元素的时候,遍历后打印出来的结果是:

可以发现,并没有录入重复的元素。

并且我们会发现,迭代器走的是中序遍历的路线。

erase()

与其他容器的erase一样,set的erase可以传入值,迭代器或者迭代器区间。

当我们要传入迭代器的时候,需要检测迭代器位置是否存在(如果find访问的val存在,返回val的迭代器;否则返回end),否则会发生崩溃:

	set<int> ::iterator pos = s.find(3);
	if (pos != s.end())
	{
		s.erase(pos);
	}

而当我们向erase中传入值得时候,即使这个值不存在,程序也不会崩溃,这是因为传入值得底层就是传入迭代器并且进行了检测。

我们发现当我们传入100的时候程序并没有崩溃。
我们也可以通过查阅文档来观察erase函数的返回值

返回一个size_type类型的变量:

它的含义是被删除节点的个数。
可能你会问,这不是一棵搜索树吗,节点只有一个,当然size_type的值就是1啊。

(4)multiset

通过上述对set得解释我们可以知道,set是一颗搜索树,通过它可以实现数据得插入去重和排序,那么如果我们不想去重呢?STL提供了一颗基于set得搜索树multiset,他没有对数据进行去重得操作。用法和set类似:

	multiset<int> s;
	s.insert(3);
	s.insert(5);
	s.insert(1);
	s.insert(1);
	s.insert(1);
	s.insert(1);
	s.insert(6);
	s.insert(9);
	s.insert(9);
	set<int>::iterator it = s.begin();
	while (it != s.end())
	{
		cout << *it << " ";
		it++;
	}

此时进行插入操作之后,再进行遍历我们发现没有发生去重操作:

我们发现遍历它依然是经过中序遍历。那么对于相同元素来说,使用find查找,找到的是哪一个值呢?

我们可以通过下面这段代码来进行尝试:

	set<int>::iterator it = s.begin();
	while (it != s.end())
	{
		cout << *it << " ";
		it++;
	}
	cout << endl;
	multiset<int>::iterator pos = s.find(1);
	while (pos != s.end())
	{
		cout << *pos << " ";
		++pos;
	}

我们可以对比这两段代码,第一段代码是从头开始遍历整个set,第二段代码是从find(1)得位置开始遍历整个set:

两者打印的结果是相同得,因此find(1)得位置其实就是set中序遍历的起始位置。

当我们使用erase函数来进行删除的时候,它删除的是所有该元素,比如我们可以测试一个erase(1),它删除了所有1:

(5)set总结

注意set只有增删查,没有修改的函数。

这是因为一旦修改了key的值,set就不是一棵搜索二叉树了。set底层的普通迭代器的实现和const迭代器的实现是一样的。*pos是一个常量。

此时会发生报错。

2.map

(1)概念

map的本质其实就是搜索二叉树的key/value模型map的每一个节点都是名为pair的结构体,pair有两个元素,其中first元素存放的是key的值,second元素存放的是value的值。

map<string, string> dict;
pair<string, string>kv1("sort", "排序");

其中dict为一颗key,value的搜索二叉树,kv1为它的一个节点。向pair的构造函数中传入key:sort和value:排序。

map的key是不支持做修改的,但是它的value是支持修改的。

(2)插入

insert的使用

map中依然有insert,不过和set不同的是,map插入的是一个pair类型的对象:

使用insert函数进行插入有三种方法

map<string, string> dict;
pair<string, string>kv1("sort", "排序");
dict.insert(kv1);//插入pair类型对象
dict.insert(pair<string, string>("string", "字符串"));//使用匿名对象进行插入
dict.insert(make_pair("test", "测试"));//使用make_pair函数进行插入

其中使用make_pair函数进行插入最为常见,他返回一个pair的匿名对象。本质上都是向map中插入带有key和value的pair。

我们可以通过水果的例子:

string arr[] = { "苹果","橘子","鸭梨","水蜜桃","苹果","橘子","鸭梨", };
map<string, int> countMap;
for (auto& str : arr)
{
	auto ret = countMap.find(str);
	if (ret == countMap.end())
	{
		countMap.insert(make_pair(str, 1));
	}
	else
	{
		ret->second++;
	}
}
for (auto e : countMap)
{
	cout << e.first << ":" << e.second << endl;
}

我们就可以使用map的insert函数来进行插入操作了。当插入失败的时候,就对ret的second进行++操作。

insert的返回值

我们可以通过查看文档来进行返回值的查看:

我们发现它返回的是一个迭代器类型,并对其进行了解释:

//insert,返回一个pair类型,pair的第一个元素是一个迭代器,它指向被插入元素;第二个元素是一个bool类型的值
//如果插入的元素已经存在,迭代器指向已经存在的元素,bool返回false
//如果插入的元素不存在,迭代器指向该元素,bool返回true

insert返回的是两个pair嵌套在一块的pair(iterator(pair<iterator(<string,int>), bool>))

了解了insert的返回值我们就可以改良一下上述代码:

for (auto& str : arr)
{
	auto kv = countMap.insert(make_pair(str, 1));
	if (!kv.second)
	{
		kv.first->second++;
	}
}
[]的重载使用

[]的引用使我们插入,删除,查找更加方便:

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

这段代码使我们插入更加方便,我们可以查阅一下[]重载的返回值:

我们来分析一下这一长条,其中mapped_type()表示的是一个匿名的value。k表示的是我们要插入的str。

分为插入成功以及插入不成功两种情况来进行讨论:

成功:当插入成功的时候,insert返回一个pair,对象调用该pair的first(即插入成功的位置)再解引用得到插入成功位置处的pair,然后再调用它的second(即value)。

失败:当插入失败的时候,insert返回一个pair,对象调用该pair的first(即相同元素的位置)解引用得到相同元素处的pair,再调用该pair的second,再对其进行操作。

其实[]与insert的区别就是,insert返回pair的second是bool值,这个是一个value的值,返回之后对这个值进行操作。

使用[]来进行插入时最常用的一种方式。它可以进行修改,插入和查找:

map<string, string> dict;
dict.insert(make_pair("left", "左边"));
dict.insert(make_pair("sort", "排序"));
dict["left"] = "剩余";//插入失败,返回已经存在pair的first,再解引用对pair的第二值进行修改
dict["test"] = "测试";//插入成功,返回插入成功pair的first,再解引用对pair的value进行赋值

(3)遍历

迭代器遍历
map<string, string>::iterator it = dict.begin();
while (it != dict.end())
{
	cout << (*it).first << ":" << (*it).second << endl;
	it++;
}

map的每一个元素是pair,it就是一个指向pair的指针,要遍历出key和value需要使用指针解引用的形式来调用pair中的first和second。

注意使用.方式进行遍历的时候,需要加(),这是因为.的优先级比*高。

范围for遍历
	for (auto& e : dict)
	{
		cout << e.first << ":" << e.second << endl;
	}

(4)删除

和set一样,可以向erase函数传入,迭代器,值或者迭代器区间来进行删除,注意传入的是key的值:

(5)multimap

同set,map也有multimap,当使用multimap的时候,二叉树的节点可以重复进行插入。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值