C++进阶---Map和Set使用及模拟实现

Posted 4nc414g0n

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了C++进阶---Map和Set使用及模拟实现相关的知识,希望对你有一定的参考价值。

Map和Set使用及模拟实现

1)引入

关联式容器与序列式容器: 关联式容器也是用来存储数据的,与序列式容器不同的是,其里面存储的是<key, value>结构的键值对,在数据检索时比序列式容器效率更高

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

pair

在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)
	
;

make_pair

make_pair:参考 make_pair

template <class T1,class T2>
pair<T1,T2> make_pair (T1 x, T2 y)

	return ( pair<T1,T2>(x,y) );


2)树形结构的关联式容器(STL分为树形结构和哈希结构)

①set

注意

注意

  1. 元素不可重复
  2. 在Set中key就是value,value也是key,set中只放value,但在底层实际存放的是由<value, value>构成的键值对
  3. RBTree作为底层容器
  4. 不允许迭代器修改Set的key/value,底层将其设置为了const(修改会造成迭代器失效),但是可以删除
  5. 在内部,set中的元素总是按照其内部比较对象(类型比较)所指示的特定严格弱排序准则(默认按照小于)进行排序
  6. set容器通过key访问单个元素的速度通常比unordered_set容器慢,但它们允许根据顺序对子集进行直接迭代
  7. set中查找某个元素,时间复杂度为log2N

迭代器

与前面容器大同小异,参考Set
迭代器遍历:

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

构造 容量(略)

与前面容器大同小异,参考Set

修改

函数功能
insert插入元素
erase删除元素

关联式容器都是使用insert/erase进行操作而非push/pop
与前面容器大同小异,参考Set

操作

函数功能
find获取元素的迭代器
count计算具有特定值的元素的个数
lower_bound将迭代器返回到下限
upper_bound将迭代器返回到上限
equal_range获取相等元素的范围

find()返回值:如果找到元素,则返回该元素的迭代器,否则返回end()
count()只能返回0或1


相当于库中的swap和容器swap区别: 参考lower_bound upper_bound equal_range

在从小到大排序数组中:
upper_bound(begin,end,num)
lower_bound(begin,end,num)
在从大到小排序数组中(重载:需要传一个比较函数):
upper_bound(begin,end,num, greater< type>())
lower_bound(begin,end,num, greater< type>())


②multiset

注意

注意:

  1. 与set的区别是,multiset中的元素可以重复,set是中value是唯一的
  2. 其他与set一致

迭代器 构造 容量 修改(略)

参考:multiset

操作

函数功能
find拿到查找元素的迭代器

由于允许重复 multiset的find找到的是中序遍历的第一个元素(底层为RBTree) 找到此元素以后,要继续看此元素的左孩子是不是同值元素,如果不是,就返回当前这个元素,如果是,继续取左边的同值元素,继续刚才的判断

验证

由于元素可以重复 这里用count可以计算个数

③map

注意

注意:

  1. map是关联容器,它按照特定的次序(按照key来比较)存储由键值key和值value组合而成的元素
  2. 在内部,map中的元素总是按照键值key进行比较排序的
  3. map中通过键值访问单个元素的速度通常比unordered_map容器慢,但map允许根据顺序对元素进行直接迭代(即对map中的元素进行迭代时,可以得到一个有序的序列)
  4. map支持下标访问符,即在[]中放入key,就可以找到与key对应的value
  5. typedef pair<const key_type,mapped_type> value_type
  6. 底层是RBTree

构造 迭代器 容量 操作(略)

参考:map

修改

函数功能
insert插入元素

pair<iterator,bool> insert (const value_type& val)
insert返回值:

  1. 如果插入成功,将pair<iterator,bool>中的iterator设为该插入元素的迭代器,bool设为true
  2. 如果插入失败说明已存在此元素,将pair<iterator,bool>中的iterator设为已存在元素的迭代器,bool设为false

参考:insert

元素访问

函数功能
operator[]访问元素
mapped_type& operator[] (const key_type& k)

	pair<iterator, bool> ret = insert(make_pair(k, mapped_type()));
	return ret.first->second;


使用[]相当于: (*((this->insert(make_pair(k,mapped_type()))).first)).second
参考insert返回值,很好理解,这里[]重载已经改变了[]的意思了
这里的mapped_type()是value的匿名对象(缺省)(相当于第一次给0),如果value是string就会调用string的匿名对象,类比


返回值可以理解为:

  1. k不存在,插入默认构造函数生成缺省值的value的pair<K, V()>
  2. k存在,返回k对应的value值

例子:

map<string, string> dict;
dict.insert(make_pair("sort", "排序"));
dict["string"] = "字符串"; // 先插入("string", ""),再修改value
dict["map"];              // 插入("map", "")
dict["map"] = "地图,映射"; // 查找+修改

统计一个数组中每个字符串出现的次数(三种)

1.普通方法

string str[] =  "sort", "sort", "tree", "insert", "sort", "tree", "sort", "test", "sort" ;
map<string, int> countMap;
for (auto& e : str)

	auto ret = countMap.find(e);
	if (ret != countMap.end())
	
		//(*ret).second++;
		ret->second++; // ret->->second++;
	
	else
	
		countMap.insert(make_pair(e, 1));
		//countMap.insert(pair<string, int>(e, 1));
	

2.使用insert

string str[] =  "sort", "sort", "tree", "insert", "sort", "tree", "sort", "test", "sort" ;
map<string, int> countMap;
for (auto& e : str)

	//pair<map<string, int>::iterator, bool> ret = countMap.insert(make_pair(e, 1));
	auto ret = countMap.insert(make_pair(e, 1));
	// 插入失败,表示e对应字符串已经在map中了,++次数
	if (ret.second == false)
		ret.first->second++;

3. 使用operator[]

string str[] =  "sort", "sort", "tree", "insert", "sort", "tree", "sort", "test", "sort" ;
map<string, int> countMap;
for (auto& e : str)
	countMap[e]++;

迭代打印结果都一样

③multimap

注意

注意:

  1. Multimaps是关联式容器,它按照特定的顺序,存储由key和value映射成的键值对<key, value>,其中多个键值对之间的key是可以重复的
  2. 注意multimap没有[]重载
  3. 其他和map相似

3)题目

leetcode: 前K个高频单词
题目:
给定一个单词列表 words 和一个整数 k ,返回前 k 个出现次数最多的单词。
返回的答案应该按单词出现频率由高到低排序。如果不同的单词有相同出现频率,按字典顺序 排序。
分析:

  1. 容易想到用sort库函数进行对value,也就是次数,加入一个仿函数进行排序,但值得注意的是这里要求按照字典序(相对顺序不能改变),所以不稳定排序(sort是快排,不稳定)一律不能用,我们只能用冒泡,归并等稳定排序

思路1:

  1. 写一个稳定排序(较麻烦 略)

思路2:

  1. 上面讲的第三种统计次数,形成一个CountMap,
  2. 再创建一个SortMultiMap(用multimap是因为次数会可能相同),不用默认的less<Key>,加上greater<Key>形成逆序,(底层由于平衡树的旋转不会改变次数相同元素的相对顺序),
  3. 再压入ret前k个SortMultiMap中的元素
vector<string> topKFrequent(vector<string>& words, int k) 
	map<string, int> CountMap;
	 for(auto& e: words)
	     CountMap[e]++;
	 multimap<int, string, greater<int>> SortMultiMap;
	 for(auto& e : CountMap)
	 
	     SortMultiMap.insert(make_pair(e.second, e.first));
	 
	 vector<string> ret;
	 for(auto& e: SortMultiMap)
	 
	     if(k==0)
	         break;
	     ret.push_back(e.second);
	     k--;
	 
	 return ret;


4)模拟实现

以上是关于C++进阶---Map和Set使用及模拟实现的主要内容,如果未能解决你的问题,请参考以下文章

C++进阶map和set模拟实现

C++进阶map和set模拟实现

C++进阶第二十篇——map和set(map和set的用法+multimap+multiset+map和set代码实现)

C++进阶第二十二篇——unordered_map和unordered_set(容器接口介绍和使用+底层代码实现)

C++之STLmap和set的模拟实现

C++——map和set的简介及使用