Lesson3:set和map

1. 关联式容器

  • 序列式容器里面存储的是元素本身,其底层为线性序列的数据结构。比如:vector,list,deque,forward_list(C++11)等。

  • 关联式容器里面存储的是<key, value>结构的键值对,在数据检索时比序列式容器效率更高。比如:set、map、unordered_set、unordered_map等。

注意 C++STL当中的stack、queue和priority_queue属于容器适配器,它们默认使用的基础容器分别是deque和vector 

2. 树形结构与哈希结构

根据应用场景的不同,C++STL总共实现了两种不同结构的关联式容器:树型结构和哈希结构。

                           关联式容器容器结构              底层实现
set、map、multiset、multimap树型结构平衡搜索树(红黑树)
unordered_set、unordered_map、unordered_multiset、unordered_multimap哈希结构哈希表

3. 键值对->pair类型

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

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

};
  • pair用了两个模板参数,set和map中某些函数的返回值是pair 

4. set的使用

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

void test_set()
{
	int a[] = { 1, 2, 1, 6, 3, 8, 5 };
	set<int> s(a, a + sizeof(a) / sizeof(int));
	//set<int, greater<int>> s(a, a + sizeof(a) / sizeof(int));

	// 排序 + 去重
	set<int>::iterator it = s.begin();
	while (it != s.end())
	{
		cout << *it << " ";
		++it;
	}
	cout << endl;

	s.erase(2);
	for (auto e : s)
	{
		cout << e << " ";
	}
	cout << endl;

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

	for (auto e : s)
	{
		cout << e << " ";
	}
	cout << endl;

	cout << s.count(3) << endl;
	cout << s.count(8) << endl;
}

int main() {
	test_set();
    return 0;
}
  • 在定义set时,不传第二个类型less<int>,默认比较方式是小于
  • set在底层是用平衡搜索树(红黑树/二插搜索树)实现的,所以在set当中查找某个元素的时间复杂度为logN。
  • set中的count函数,本来是统计这个元素在set中出现的次数,但是二叉树的值不能有重复的
    所以它的返回不是1就是0

4.1  lower_bound && upper_bound

 

#include <iostream>
//#include <functional>
#include <map>
#include <set>
#include <string>
using namespace std;

void test_set2()
{
	std::set<int> myset;
	std::set<int>::iterator itlow, itup;

	for (int i = 1; i < 10; i++)
		myset.insert(i * 10); // 10 20 30 40 50 60 70 80 90

	itlow = myset.lower_bound(25); // >= val
	itup = myset.upper_bound(60);  // > val             //
	cout << "[" << *itlow << "," << *itup << ")" << endl;

	// 删掉一段迭代器区间(左闭右开)
	myset.erase(itlow, itup);                     // 10 20 70 80 90

	std::cout << "myset contains:";
	for (std::set<int>::iterator it = myset.begin(); it != myset.end(); ++it)
		std::cout << ' ' << *it;
	std::cout << '\n';
}


int main() {
	test_set2();
	return 0;
}

  • 一个迭代器的区间通常是左闭右开的 
  •  lower_bound(val)返回这个set中第一个>=val值的跌迭代器
  • upper_bound(val)返回这个set中第一个>val值的跌迭代器

4.2 equal_range 

#include <iostream>
//#include <functional>
#include <map>
#include <set>
#include <string>
using namespace std;

void test_set2()
{
	std::set<int> myset;

	for (int i = 1; i <= 5; i++) {
		myset.insert(i * 10);   // myset: 10 20 30 40 50
	}
	//std::pair<std::set<int>::const_iterator, std::set<int>::const_iterator> ret;
	pair<set<int>::const_iterator, set<int>::const_iterator> ret;
	ret = myset.equal_range(40); // xx <= val < yy

	std::cout << "the lower bound points to: " << *ret.first << '\n';
	std::cout << "the upper bound points to: " << *ret.second << '\n';
}

int main() {
	test_set2();
    return 0;
}

 

  •  返回一个范围的边界,该范围包含容器中所有等效于val的元素

4.3 了解multiset(允许出现重复节点)

#include <iostream>
//#include <functional>
#include <map>
#include <set>
#include <string>
using namespace std;

void test_set3()
{
	int a[] = { 3,1, 2, 1, 6, 3, 8,3, 5,3 };
	multiset<int> s(a, a + sizeof(a) / sizeof(int));

	// 排序
	for (auto e : s)
	{
		cout << e << " ";
	}
	cout << endl;

	cout << s.count(3) << endl;

	// find时,如果有多个值,返回中序的第一个
	auto pos = s.find(3);
	while (pos != s.end())
	{
		cout << *pos << " ";
		++pos;
	}
	cout << endl;

	// 删除所有的3
	s.erase(3);
	for (auto e : s)
	{
		cout << e << " ";
	}
	cout << endl;

	pos = s.find(1);
	if (pos != s.end())
	{
		s.erase(pos);
	}
	for (auto e : s)
	{
		cout << e << " ";
	}
	cout << endl;
}

int main() {
	test_set3();
    return 0;
}
  • multiset和set类似,本质也是一颗二插搜索树,
  • 不同点在:这棵树允许出现重复节点
  • multiset的find函数,如果有多个值,返回中序的第一个
  • multiset的erase函数,如果删除的是val,那么删除这棵二叉树中所有的val

5. map的使用

#include <iostream>
#include <functional>
#include <map>
#include <set>
using namespace std;
void test_map1()
{
	map<string, string> dict;

	dict.insert(pair<string, string>("sort", "排序"));

	typedef pair<string, string> DictKV;
	dict.insert(DictKV("string", "字符串"));

	dict.insert(make_pair("left", "左边"));// 推荐

	//map<string, string>::iterator it = dict.begin();
	auto it = dict.begin();
	
	while (it != dict.end())
	{
		//cout << (*it).first << (*it).second <<endl;
		cout << it->first << it->second << endl;
		++it;
	}
	cout << endl;

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

int main() {
	test_map1();
    return 0;
}

  •  map的底层是一种映射,是一种键值对
  • map的begin函数返回的是一个迭代器,但这个迭代器是一个pair类型的指针,
    pair的成员是first(Key)和second(Value),是键值对的关系 
  • make_pair函数返回的是pair类型,就像是构造了一个pair
  • map在底层是用平衡搜索树(红黑树)实现的,所以在map当中查找某个元素的时间复杂度为logN

5.1 []运算符  

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

void test_map1()
{
	string arr[] = { "苹果", "西瓜", "苹果", "西瓜", "苹果", "苹果", "西瓜", "苹果", "香蕉", "苹果", "香蕉" };
	map<string, int> countMap;
	for (auto& str : arr)
	{
		// 1、str不在countMap中,插入pair(str, int()),然后在对返回次数++
		// 2、str在countMap中,返回value(次数)的引用,次数++;
		countMap[str]++;
	}

	map<string, int>::iterator it = countMap.begin();
	while (it != countMap.end())
	{
		cout << it->first << ":" << it->second << endl;
		++it;
	}
	cout << endl;

	map<string, string> dict;
	dict.insert(make_pair("sort", "排序"));
	dict["insert"];
	dict["insert"] = "插入";
	dict["left"] = "左边";

	multimap<string, string> mdict;
	mdict.insert(make_pair("sort", "排序"));
	mdict.insert(make_pair("left", "左边"));
	mdict.insert(make_pair("left", "左边"));
	mdict.insert(make_pair("left", "剩余"));
}

int main()
{
	test_map1();

	return 0;
}
  •  map中这个key,返回value的引用,此时map[]的作用是查找,修改value
  • map中没有这个key,就会插入一个pair(key,V(),调用它的默认构造函数,返回vaule的引用 
    此时map[]的作用是插入,修改value
  • map不存在重复键值对

5.2 map的[]运算符底层实现

insert的返回值 

  • key已经在map中,返回pair(key_iterator,false);
  • key不在map中,返回pair(new_key_itetator,true);
  •  insert的返回值类型,是为了[]运算符做准备

 简单实现一下

  •   []的底层是调用的insert接口

5.3 了解multimap(允许出现重复键值对) 

  • multimap除了允许出现重复键值对之外,其他的和map相同
  • 由于multimap容器允许键值冗余,调用[ ]运算符重载函数时,应该返回键值为key的哪一个元素的value的引用存在歧义,因此在multimap容器当中没有实现[ ]运算符重载函数 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值