C++进阶之map与set

27 篇文章 11 订阅
订阅专栏

前言:本篇博客只介绍这两个容器比较难的接口,其余接口可以通过官方文档去查询了解

文档入口:cplusplus.com - The C++ Resources Networkicon-default.png?t=M4ADhttp://m.cplusplus.com/

目录

一、关联式容器

二、键值对

树形结构的关联式容器

set

set的介绍

set的使用

1. set的模板参数列表

2.set的使用

3.erase

multiset

multiset的介绍

multiset的使用

multiset中面对重复的数据它先找到的是那一个重复的数据呢? 

如何删除所有的重复数据?

我们发现set/multiset是没有修改接口的,我们可以借助迭代器进行修改吗?

map

map的介绍

1. map的模板参数说明

2.Pair

map的使用

insert

map的遍历

[ ]的使用

multimap

multiset的介绍

multimap的使用

cout

erase

练习题

一、统计前k种水果

方法一:

方法一的优化:

方法二:

方法三:

​编辑方法三的优化:

二、前K个高频单词 


一、关联式容器

STL中将数据结构叫做容器。我们已经接触过STL中的部分容器,比如:vectorlistdequeforward_list(C++11)等,这些容器统称为序列式容器,因为其底层为线性序列的数据结构,里面存储的是元素本身。那什么是关联式容器?它与序列式容器有什么区别?

关联式容器 也是用来存储数据的,与序列式容器不同的是,其 里面存储的是 <key, value> 结构的键值对,在 数据检索时比序列式容器效率更高关联史容器:map/set.. 数据之间有强烈的关联性底层是搜索树。

ps:栈和队列是适配器

二、键值对

用来表示具有一一对应关系的一种结构,该结构中一般只包含两个成员变量keyvaluekey代表键值,value表示与key对应的信息。比如:现在要建立一个英汉互译的字典,那该字典中必然有英文单词与其对应的中文含义,而且,英文单词与其中文含义是一一对应的关系,即通过该应该单词,在词典中就可以找到与其对应的中文含义。

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

树形结构的关联式容器

根据应用场景的不同, STL 总共实现了两种不同结构的关联式容器:树型结构与哈希结构。 树型结构的关联式 容器主要有四种: map set multimap multiset 。这四种容器的共同点是:使用平衡搜索树 ( 即红黑树 )作为其底层结果,容器中的元素是一个有序的序列。下面一依次介绍每一个容器。

set

set的介绍

set的官方文档介绍: set - C++ Reference (cplusplus.com)

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

set的使用

1. set的模板参数列表

T: set 中存放元素的类型,实际在底层存储 <value, value> 的键值对。
Compare set 中元素默认按照小于来比较(就是个仿函数)
Alloc set 中元素空间的管理方式,使用 STL 提供的空间配置器管理

2.set的使用

void test_set1()
{
	//排序+去重
	set<int> s;
	s.insert(3);
	s.insert(1);
	s.insert(8);
	s.insert(2);
	s.insert(5);
	s.insert(5);
	s.insert(5);

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

排序加去重,排序也就是进行中序遍历。

3.erase

删除一个位置必须保证删除的位置的迭代器是有效的。

删除一个值

这个地方为什么不用bool值呢?这就与multiset有关。

multiset

multiset的介绍

multiset的官方文档介绍: multiset - C++ Reference (cplusplus.com)

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

multiset的使用

此处只简单演示setmultiset的不同,其他接口接口与set相同,大家可参考set

如果我们不想去重仅仅就想排序该怎么办呢?

multi的意思是多样的,多种的。它依然是在我们set里面的。

我们发现multiset是不进行去重操作的,multi就是多种多样,允许我们的数据有重复,冗余。

multiset中面对重复的数据它先找到的是那一个重复的数据呢? 

  

我们用find进行验证。find的val有多个值的时候,返回中序第一个val值所在节点的迭代器。

 我们将1改成8再次验证。

答案就是返回中序遍历的第一个重复值。

如何删除所有的重复数据?

在这里我们以删除1为例

因为找到的1是从中序的第一个位置开始,所以就可以进行遍历,只要判断出pos是1就将它删除,知道遍历结束。但是我们目前的写法是有问题的。

程序崩溃了。 

它是二叉树,它的迭代器类似于节点的指针,当第一个1这个节点被干掉了,它里面的pos就成了野指针,++pos是找不到下一个位置的。

方法1:

解决方法:定义一个next,将pos赋值给next,对next++,然后删除pos,再将next赋值给pos,也就是提前保存好pos的值。

方法2:用值去删

所以这时候就明白为什么erase的返回值是返回被删除值的个数,对于set没意义,对于multiset就有意义了。

有几个就去删几个。 

可以认为这个erase的实现就是依据方法1实现二来的,在方法一中加个计数器,进行封装,本质都是一样的。

我们发现set/multiset是没有修改接口的,我们可以借助迭代器进行修改吗?

经过验证是不可以进行修改的。*pos是个常量。如何做到的呢?

这里的迭代器要去调用operator*和operator->,他俩返回const T的引用就可以了。也就是set的底层普通迭代器和const迭代器在这个地方的实现是一样的,都不允许修改。因为修改了以后就不能保证你还是一个搜索树了。

map

map的介绍

1. map的模板参数说明

  • key: 键值对中key的类型
  • T: 键值对中value的类型
  • Compare: 比较器的类型,map中的元素是按照key来比较的,缺省情况下按照小于来比较,一般情况下(内置类型元素)该参数不需要传递,如果无法比较时(自定义类型),需要用户自己显式传递比较规则 (一般情况下按照函数指针或者仿函数来传递)
  • Alloc:通过空间配置器来申请底层空间,不需要用户传递,除非用户不想使用标准库提供的空间配置器
  • 注意:在使用map时,需要包含头文件。

2.Pair

map这我们首先得了解pair,因为map是标准的key,value结构,也就是每个节点的位置除了存key,还存了value,那map的key,value是怎么存的呢?它和我们之前实现的不太一样,并不是给一个key给一个value,而是将keyvalue封装到了一个类结构里面去,这个结构叫做pair,pair也叫做键值对。

pair本身的结构也是一个类

 里面有两个成员,

first就是带一个模板参数也就是key,second就是第二个模板参数也就是value。

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

map的使用

insert

这里我们主要讨论插入的方法1

insert的是一个value_type,value_type就是一个pair。pair第一个参数就是key,第二个参数是mapped_type也就是T,也就是value。

第一种插入方法:

第二种插入方法:

第三种插入方法:借助make_pair 

make_pair

是一个函数模板,返回值是pair,返回pair的匿名对象。

map的遍历

像往常一样写时会报错的

解引用返回节点里面的数据,节点里面是数据不是一个值,而是把key和value放到了一个结构里去,也就是pair,所以它的返回值是pair,这里不支持输出pair,因为pair没有重载流提取和流插入 

正确方法:将它的两个值都打印出来 

 或者使用->两者更常用下面这种

同时可以用范围for

这里的打印出来还是有排序的意思的,按照key去排序,这里我们的key是string类型,所以按照ascll码去排序。 

[ ]的使用

我们假设现在要统计水果的次数

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

因为key修改会影响树的结构,但value修改是不影响的。

传统写法:

void test2_map()
{
	string arr[] = { "苹果","苹果","香蕉","苹果","香蕉","苹果","樱桃" };

	//key是水果的名称,value是次数
	map<string, int> countMap;
	for (auto& str : arr)
	{
		//看下水果有没有出现过,如果没有出现过我们就插入,如果已经出现过,就进行修改
		auto ret = countMap.find(str);
		if (ret == countMap.end()) //表示没有找到
		{
			countMap.insert(make_pair(str, 1));
		}
		else
		{
			//find返回的是迭代器
			ret->second++;
		}
	}
	for (auto& kv : countMap)  
	{
		cout << kv.first << ":" << kv.second << endl;
	}
}

缺陷:如果key不在的情况下,查找会走一次这个搜索树,插入的时候也会走一次这个搜索树。

 优化:

首先明白insert的返回值

 insert的第一个重载有一个返回值,它返回的不是单纯的真假,返回一个pair,pair里的bool很好理解如果key已经有了返回false,没有返回true,那这个迭代器该如何理解呢?

也就是说如果key没有,插入后返回true,迭代器指向新插入的这个节点。

如果key已将存在,则返回false,迭代器还是指向原来的这个节点。     

ps:迭代器含义    

void test2_map()
{
	string arr[] = { "苹果","苹果","香蕉","苹果","香蕉","苹果","樱桃" };

	//key是水果的名称,value是次数
	map<string, int> countMap;

	for (auto& str : arr)
	{
		auto kv = countMap.insert(make_pair(str, 1)); //先不管你在不在,直接插入
		//一种情况是这个水果不在,插入成功,给1次 二、这个水果已经出现过,插入失败应该对次数++
		if (kv.second == false)
		{
			kv.first->second++;
		}
	}

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

 这个方法最大有点就是insert既充当了插入,又充当了查找。

方法二:

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

原理解释: 

之前我们的[ ] 支持的是随机访问,但是这里的[ ]肯定不是随机访问,因为它是一颗树。

 同时它还等价于这个,大概认为它是这一样实现的

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

我们简化下这句代码:

我们结合我们统计水果次数的例子深入理解一下:

这里我们的key_type就是string ,mapped_type就是int ,他去调用insert的时候,我也不知道水果到底出现过了没,所以我们先插入,这里的k就给的是水果,value给的是mapped_type的匿名对象,这个匿名对象对于int来说就是0(C++对内置类型也进行了升级,使得他们也有默认的构造函数),插入的时候可能插入成功或者插入失败,如果这个水果没有就插入成功了,插入成功后就返回pair,pair的迭代器就是新插入水果节点的迭代器,再对这个迭代器解引用就是(我们的简化版就是直接用的箭头)这个节点里面所在水果的key,value,key是刚插入的水果,value是0。而且这里返回的是这个次数的引用。刚开始返回的是0次,countMap[str]++就变成了1次,这个++是作用在函数调用的返回值上面的;第二次苹果再来了以后,insert的时候,k就给的是水果,value给的是mapped_type的匿名对象,还是0。但此时苹果已经有了,会插入失败,迭代器就返回之前那个苹果的迭代器,上次苹果中的次数已经变成1次了,然后返回第一次苹果的节点里面的second的别名,再进行countMap[str]++,就变成了2次。以此类推...

[ ]的功能

1、插入 2、查找(key对应value) 3、修改(key对应value)

如果水果第一次出现,对应的是插入+修改,水果不是第一次出现,对应查找+修改

对应用法举例

 但是建议查找最好还是用find,因为如果没有key,[ ]就成了插入。

multimap

multiset的介绍

multiset的官方文档介绍: multimap - C++ Reference (cplusplus.com)

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

multimap的使用

multimap 中的接口可以参考 map ,功能都是类似的。
注意:
  • 1. multimap中的key是可以重复的。
  • 2. multimap中的元素默认将key按照小于来比较
  • 3. multimap中没有重载operator[]操作
  • 4. 使用时与map包含的头文件相同:

同样是允许数据冗余

multimap就没有[ ]了,因为我现在有很多的key,到时候返回的时候我就不知道到底该返回那个key对应的value。其他操作都一样

cout

cout的作用也是查找,但更大的意义是用来计数,适合multimap使用

erase

multimap中的erase就是把所有的key都删除调

练习题

一、统计前k种水果

1. 本公司现在要给公司员工发波福利,在员工工作时间会提供大量的水果供员工补充营养。由于水果种类比较多,但是却又不知道哪种水果比较受欢迎,然后公司就让每个员工报告了自己最爱吃的k 种水果,并且告知已经将所有员工喜欢吃的水果存储于一个数组中。然后让我们统计出所有水果出现的次数,并且求出大家最喜欢吃的前k 种水果。

以该组数据为例,k值传3. 

思路:利用一个map(countMap)进行统计次数,然后在进行排序,取出前k种即可。

方法一:

统计次数我们已轻车熟路,重要的就是进行排序,我们首先使用sort进行排序,但是sort要求的是随机迭代器,因为sort底层是快排,需要三数取中,也就是需要迭代器可以做减法操作,显然这里传map的迭代器是不行的。

但是我们可以将countMap中的数据放到vector中,sort是支持vector的迭代器的。

但此时我们排序的结果是这样的,那么pair支持排序吗? 

pair是支持比较大小的,但是它比较大小是先比first,first相等再去比second

但是我们不想去比first,只需要去比较second,我们写个仿函数就可以解决了。

仿函数:

struct CountVal
{
	//仿函数最大的优势就是你可以控制它排序的方式
	bool operator()(const pair<string, int>& l, const pair<string, int>& r)
	{
		return l.second > r.second; //>就是降序 ,<就是升序
	}
};

完整代码演示:

struct CountVal
{
	//仿函数最大的优势就是你可以控制它排序的方式
	bool operator()(const pair<string, int>& l, const pair<string, int>& r)
	{
		return l.second > r.second; //>就是降序 ,<就是升序
	}
};

void GetFavoriteFruit(const vector<string>& fruits, size_t k)
{
	//统计次数
	map<string, int> countMap;
	for (auto& str : fruits)
	{
		countMap[str]++;
	}

    //排序
	vector<pair<string, int>> sortV;
	for (auto& kv : countMap)
	{
		sortV.push_back(kv);
	}
	sort(sortV.begin(), sortV.end(), CountVal());//这里的sort是不知道如何排pair,pair支持比较吗?
	for (auto&kv : sortV)
	{
		cout <<kv.first<<":"<<kv.second << endl;
	}
	cout << endl;
	//取出前k个
	for (int i = 0; i < k; i++)
	{
		cout << sortV[i].first << ":" << sortV[i].second << endl;
	}
}

方法一的优化:

目前我们方法一存在的问题:sortV插入数据的时候得开很大的空间,如果pair里面存的很大的话。同时存在深拷贝问题。

优化方法:pair很大,但是迭代器很小,里面只存一个节点的指针,直接在vector里面存map的迭代器,只有4或者8个字节。所以我们要对仿函数进行修改,vector中存的是迭代器则访问成员时使用->即可。

完整代码:

struct CountValiterator
{
	//仿函数最大的优势就是你可以控制它排序的方式
	bool operator()(const map<string, int>::iterator& l, const map<string, int>::iterator& r)
	{
		return l->second > r->second; //>就是降序 ,<就是升序
	}
};
void GetFavoriteFruit2(const vector<string>& fruits, size_t k)
{
	//统计次数
	map<string, int> countMap;
	for (auto& str : fruits)
	{
		countMap[str]++;
	}

	//上面方法存在的问题:sortV插入数据的时候得开很大的空间,如果pair里面存的很大的话
	vector<map<string,int>::iterator> sortV; //pair很大,但是迭代器很小,里面只存一个节点的指针,直接在vector里面存map的迭代器,只有4或者8个字节
	auto it = countMap.begin();
	while (it != countMap.end())
	{
		sortV.push_back(it);
		++it;
	}
    
	sort(sortV.begin(), sortV.end(), CountValiterator());//这里的sort是不知道如何排pair,pair支持比较吗?
	for (int i = 0; i < k; i++)
	{
		cout << sortV[i]->first << ":" << sortV[i]->second << endl;
	}
}

方法二:

 利用multimap进行排序,因为它不会去重,但这里我们是要对数字进行排序,所以要让map中的value,做multimap中的key。但multimap默认排序时升序,因为它compare的缺省值是less<key>

取出前三种水果可以用反向迭代器倒着取遍历。

如果不使用反向迭代器,我们就可以使用仿函数:great<key>官方提供的降序排序

完整代码(使用反向迭代器)

void GetFavoriteFruit3(const vector<string>& fruits, size_t k)
{
	//统计次数
	map<string, int> countMap;
	for (auto& str : fruits)
	{
		countMap[str]++;
	}

	//排序,它不会去重
	multimap<int, string> sortMap;
	for (auto& kv : countMap)
	{
		sortMap.insert(make_pair(kv.second, kv.first));
	}

	//取出前三种水果可以用反向迭代器倒着取遍历
	auto it = sortMap.rbegin();
	while (it != sortMap.rend() && k != 0)
	{
		cout << it->second << ":" << it->first << endl;
		++it;
		k--;
	}
	
}

完整代码(不使用反向迭代器)

void GetFavoriteFruit3(const vector<string>& fruits, size_t k)
{
	//统计次数
	map<string, int> countMap;
	for (auto& str : fruits)
	{
		countMap[str]++;
	}
	
	//取出前三种水果,不使用反向迭代器,就使用仿函数
	multimap<int, string, greater<int>> sortMap;
	for (auto& kv : countMap)
	{
		sortMap.insert(make_pair(kv.second, kv.first));
	}

	for (auto& kv : sortMap)
	{
		if (k)
		{
			cout << kv.second << ":" << kv.first << endl;
			k--;
		}
	}

}

方法三:

利用优先级队列也就是堆

struct CountValpq
{
	//仿函数最大的优势就是你可以控制它排序的方式
	bool operator()(const pair<string, int>& l, const pair<string, int>& r)
	{
		return l.second < r.second; //>建大堆 ,<就是建小堆
	}
};
void GetFavoriteFruit4(const vector<string>& fruits, size_t k)
{
	//统计次数
	map<string, int> countMap;
	for (auto& str : fruits)
	{
		countMap[str]++;
	}
    
	//利用优先级队列--堆
	priority_queue < pair<string, int>, vector<pair<string, int>>, CountValpq>  pq; //类模板传类型,函数模板传对象
	for (auto& kv : countMap)
	{
		pq.push(kv);
	}

	while (k--)
	{
		cout << pq.top().first << ":" << pq.top().second << endl;
		pq.pop();
	}
	cout << endl;

}

方法三的优化:

类似于方法一的优化我们改为传迭代器

struct CountValiteratorpq
{
	//仿函数最大的优势就是你可以控制它排序的方式
	bool operator()(const map<string, int>::iterator& l, const map<string, int>::iterator& r)
	{
		return l->second < r->second; //>建大堆 ,<就是建小堆
	}
};
void GetFavoriteFruit5(const vector<string>& fruits, size_t k)
{
	//统计次数
	map<string, int> countMap;
	for (auto& str : fruits)
	{
		countMap[str]++;
	}

	//利用优先级队列--堆
	priority_queue < map<string, int>::iterator, vector<map<string, int>::iterator>, CountValiteratorpq>  pq; //类模板传类型,函数模板传对象
	auto it = countMap.begin();
	while (it != countMap.end())
	{
		pq.push(it);
		++it;
	}

	while (k--)
	{
		cout << pq.top()->first << ":" << pq.top()->second << endl;
		pq.pop();
	}
	cout << endl;

}

二、前K个高频单词 

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

示例 1:

输入: words = ["i", "love", "leetcode", "i", "love", "coding"], k = 2
输出: ["i", "love"]
解析: "i" 和 "love" 为出现次数最多的两个单词,均为2次。
    注意,按字母顺序 "i" 在 "love" 之前。

示例 2:

输入: ["the", "day", "is", "sunny", "the", "the", "the", "sunny", "is", "is"], k = 4
输出: ["the", "is", "sunny", "day"]
解析: "the", "is", "sunny" 和 "day" 是出现次数最多的四个单词,
    出现次数依次为 4, 3, 2 和 1 次。

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/top-k-frequent-words
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

来源:力扣(LeetCode)
链接:https://leetcode.cn/problems/top-k-frequent-words
著作权归领扣网络所有。商业转载请联系官方授权,非商业转载请注明出处。

在讲过上道题以后,这个题就显得比较简单了,但是不同的是这道题的难度在于排完序后,如果k值相同,还要将它们按照字符顺序进行输出。也就是说如果k值相同还要将他们按照ascii码值进行排序。这里我们用sort就不行了,因为sort的底层是快排,所以它是一个不稳定的排序。(稳定性博主在讲排序的时候介绍过)。

解决方法:博主在这采用的是multimap进行排序,因为起初用map统计的时候,map会对key进行排序,对应到这道题map就默认会将这些字符串按照ASCII码进行排序。之后将map中的数据放到multimap中的时候是会按照这些字符串的顺序进行插入的,然后根据int值进行比较。所以就解决了稳定性的问题。

完整代码演示:

class Solution {
public:
    vector<string> topKFrequent(vector<string>& words, int k) {
        
        //统计次数
        map<string, int> countMap;
        for(auto&str : words)
        {
            countMap[str]++;
        }

        //排序 
        multimap<int, string, greater<int>> sortMap;
        for(auto&kv : countMap)
        {
            sortMap.insert( make_pair(kv.second, kv.first) );
        }
         
        //排好序后放到vector中 
        vector<string> v;
        for(auto&kv : sortMap)
        {
            if(k)
            {
                v.push_back(kv.second);
                k--;
            }
        }

        return v; 
    }
};

其他解决方法:既然sort不能用,但是我们可以使用stable_sort,这个算法就可以保持稳定性

带你深入理解STL之SetMap
ZeeCoder
09-09 1万+
在上一篇博客中,讲到了STL中关于红黑树的实现,理解起来比较复杂,正所谓前人种树,后人乘凉,RBTree把树都种好了,接下来就该setmap这类关联式容器来“乘凉”了。STL的setmap都是基于红黑树实现的,和stack和queue都是基于deque一样,它们仅仅是调用了RBTree提供的接口函数,然后进行外层封装即可。本篇博客理解起来比较轻松,setmap的源代码也不多,大家可以慢慢“品味
mapset的模拟实现
最新发布
06-06
封装红黑树,模拟SLT源码实现,封装出mapset
C++进阶宝典.docx
08-12
C++进阶课程讲义_v1.0.4
c++关联容器用法详解(setmap
01-27
关联容器与序列容器有着根本性的不同,序列容器的元素是按照在容器中的位置来顺序保存和访问的,而关联容器的元素是按关键元素来保存和访问的。关联容器支持高效的关键字查找与访问。两个主要的关联容器类型是mapset。 1.1简介:set里面每个元素只存有一个key,它支持高效的关键字查询操作。set对应数学中的“集合”。 1.2特点: 储存同一类型的数据元素(这点和
C++ - setmap详解
大海洋的博客
04-15 2396
详细介绍了 set 以及 map.
c++进阶讲义.pdf
05-26
c++提供了函数模板(function template.)所谓函数模板,实际上是建立一个通用函数
c++容器list、vector、mapset区别与用法详解
08-19
主要介绍了c++容器list、vector、mapset区别与用法详解,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
C++ STL中,mapset有什么区别,分别又是怎么实现的?
学无止尽,谨言慎行!
04-03 5377
mapset都是C++的关联容器,其底层实现都是红黑树(RB-Tree)。由于 mapset所开放的各种操作接口,RB-tree 也都提供了,所以几乎所有的 mapset的操作行为,都只是转调 RB-tree 的操作行为。 mapset区别在于: (1)map中的元素是key-value(关键字—值)对:关键字起到索引的作用,值则表示与索引相关联的数据;Set与之相对就是关键字的简单集...
C++】详解setmap
Hush_H的博客
07-05 470
详解setmap,及其使用,详解setmultiset区别mapmultimap区别
C++mapset
在平淡的日子里寻找存在的价值
03-24 1045
map/multimap 的使用以及 set/multiset 的使用
C++——mapset
fsbwb的博客
10-25 108
set是按照一定次序存储元素的容器·在set中,元素的value也标识它(value就是key,类型为T),并且每个value必须是唯一的。set中元素不能在容器中修改,但是可以从容器中插入或删除他们。·在内部,set中元素总是按照其内部比较对象(类型比较)所指示的特定严格弱排序准则进行排序。·set容器通过key访问单个元素的速度通常比undordered_set容器慢,但它们允许根据顺序对子集进行直接迭代。·set在底层使用搜索二叉树(红黑树)实现的。
C++容器篇,setmap容器
CHAKMING1的博客
01-14 1051
介绍了C++的关联式容器setmap,介绍了其中的接口如何使用,底层原理是什么。最后,用红黑树简单实现了setmap
C++进阶课程讲义_v1.0.pdf
09-05
C++进阶课程讲义_v1.0
C++进阶,迈向C++高级世界
10-12
C++进阶,迈向C++高级世界
这是一个c++进阶编程的文档
03-08
这是一个c++进阶编程的文档,包含了实现stl容器,C++的内存管理,深度探索c++对象模型,ACE网络编程,UNIX网络编程,多线程编程,模板的扩展使用,c专家编程,C的缺陷和漏洞.zip这是一个c++进阶编程的文档,包含了...
c++进阶(stl资源),非原创,配套b站(C++进阶之STL)教程
02-18
参加竞赛有帮助,有六大容器的讲解和一些算法,csp认证可以参考或其他
C++mapset的使用与区别
m0_37154839的博客
12-15 1184
set set是一种关联式容器,其特性如下: set以RBTree作为底层容器 所得元素的只有key没有value,value就是key 不允许出现键值重复 所有的元素都会被自动排序 不能通过迭代器来改变set的值,因为set的值就是键 针对这五点来说,前四点都不用再多作说明,第五点需要做一下说明。如果set中允许修改键值的话,那么首先需要删除该键,然后调节平衡,在插入修改后的键值,再...
C++setmap
m0_62179366的博客
11-06 7194
mapset也十分的相似,map是K/V的模型,set是K的模型map也有两种区别setmultiset相同,只是键值是否允许冗余的差别不过map的插入就比较特别了我们发现insert的参数是一个value_type类型然后我们发现它实际上是一个pair{T1 first;T2 second;{}{}};用来表示具有一一对应关系的一种结构,该结构中一般只包含两个成员变量key和value,key代表键值,value表示与key对应的信息。
深入篇【C++setmap(multiset/multimap)特性总结与使用
Extreme_wei的博客
08-26 513
深入篇【C++setmap(multiset/multimap)特性总结与使用。set/map--->关联式容器?树形结构的关联式容器!带你了解C++的魅力!
c++ map set
08-21
- *1* *2* *3* [C++进阶mapset](https://blog.csdn.net/qq_52433890/article/details/124850598)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_...

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

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
写文章

热门文章

  • 建堆的时间复杂度详解 7604
  • 三子棋(井字棋)的实现 5911
  • 网络协议中各层的传输单位 5080
  • 数据结构之【顺序表的实现(详解)】 4319
  • 网易C++实习一面 4094

分类专栏

  • Linux 9篇
  • C++ 27篇
  • 项目
  • 网络基础 6篇
  • 数据结构 16篇
  • C语言 6篇

最新评论

  • 数据结构之【堆排序】

    cls-evd: void heapSort(vector<int>& v) { vector<int> tmp; int size = v.size(); int n = 0; while (n < size) { //建堆是O(N) for(int i = (v.size() - 1 - 1) / 2; i >= 0; i--) { Adjustdown(v, v.size(), i); } tmp.push_back(v[0]); v.erase(v.begin()); n++; if (v.size() == 1) { break; } } tmp.push_back(v[0]); v.clear(); for (auto e : tmp) { v.push_back(e); } } 这个就是通过建小堆排升序的方式,显然是n的平方

  • C++11

    坚持不懈的大白: 支持一下,写的太详细了吧!

  • Linux之进程控制

    John_Snowww: 博主写的很好啊,非常详细

  • 网络协议中各层的传输单位

    陈跃光: Thanks♪(・ω・)ノ

  • 数据结构之【堆排序】

    mx_boom: 我想问为什么是n²,明明从第二个开始选直到最后一个,循环n-1次,第一个数和第二个数只需要一次,第三个第四个第五个第六个只需要两次,逐行建堆,最终明明只需要n*logn,我不明白n²怎么来的。代码例:for(int i =1; i < n; i++) 循环体内是向上建堆代码,建不出小堆?还是时间复杂度不对?

大家在看

  • 电子文档盖骑缝章软件 173
  • 51单片机学习记录-08-蜂鸣器(ULN2003双极行线性集成电路) 302
  • 有没有一个特别厉害的Java大佬帮我做一道题啊啊啊啊啊啊
  • 公司面试题总结(一) 44
  • Python入门

最新文章

  • C++之deque
  • C++之stack与queue的模拟实现
  • extern “C“
2024年4篇
2023年22篇
2022年24篇
2021年17篇

目录

目录

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值

深圳SEO优化公司濮阳网站设计湖州网站建设多少钱漯河百姓网标王推广报价惠州营销型网站建设许昌百度网站优化公司抚顺网站优化按天收费公司邯郸网站seo优化报价永州网站优化按天扣费多少钱焦作百度关键词包年推广南平至尊标王哪家好凉山优秀网站设计价格仙桃网站定制推荐上海企业网站改版忻州seo网站推广多少钱扬州网站推广方案营口至尊标王哪家好芜湖网站优化按天计费临猗网络广告推广推荐许昌优秀网站设计推荐林芝网站设计推荐长治网站优化按天扣费哪家好酒泉建设网站公司桐城模板网站建设报价楚雄网站定制报价芜湖seo网站推广推荐河源网站优化按天扣费报价铜川优化公司日照模板推广哪家好南平网站搭建福田建网站报价歼20紧急升空逼退外机英媒称团队夜以继日筹划王妃复出草木蔓发 春山在望成都发生巨响 当地回应60岁老人炒菠菜未焯水致肾病恶化男子涉嫌走私被判11年却一天牢没坐劳斯莱斯右转逼停直行车网传落水者说“没让你救”系谣言广东通报13岁男孩性侵女童不予立案贵州小伙回应在美国卖三蹦子火了淀粉肠小王子日销售额涨超10倍有个姐真把千机伞做出来了近3万元金手镯仅含足金十克呼北高速交通事故已致14人死亡杨洋拄拐现身医院国产伟哥去年销售近13亿男子给前妻转账 现任妻子起诉要回新基金只募集到26元还是员工自购男孩疑遭霸凌 家长讨说法被踢出群充个话费竟沦为间接洗钱工具新的一天从800个哈欠开始单亲妈妈陷入热恋 14岁儿子报警#春分立蛋大挑战#中国投资客涌入日本东京买房两大学生合买彩票中奖一人不认账新加坡主帅:唯一目标击败中国队月嫂回应掌掴婴儿是在赶虫子19岁小伙救下5人后溺亡 多方发声清明节放假3天调休1天张家界的山上“长”满了韩国人?开封王婆为何火了主播靠辱骂母亲走红被批捕封号代拍被何赛飞拿着魔杖追着打阿根廷将发行1万与2万面值的纸币库克现身上海为江西彩礼“减负”的“试婚人”因自嘲式简历走红的教授更新简介殡仪馆花卉高于市场价3倍还重复用网友称在豆瓣酱里吃出老鼠头315晚会后胖东来又人满为患了网友建议重庆地铁不准乘客携带菜筐特朗普谈“凯特王妃P图照”罗斯否认插足凯特王妃婚姻青海通报栏杆断裂小学生跌落住进ICU恒大被罚41.75亿到底怎么缴湖南一县政协主席疑涉刑案被控制茶百道就改标签日期致歉王树国3次鞠躬告别西交大师生张立群任西安交通大学校长杨倩无缘巴黎奥运

深圳SEO优化公司 XML地图 TXT地图 虚拟主机 SEO 网站制作 网站优化