序列式容器:
在初阶,我们接触过STL部分容器,如: vector、list、deque、forward_list(C++11)等,这些容器被称为序列式容器。
原因:这些容器底层结构为线性序列的数据结构,里面存储的是元素本身。
关联式容器:
关联式容器也是用来存储数据的。
原因:关联式容器与序列式容器不同的是,其里面存储的是
概念:用来表示具有一一对应关系的一种结构,该结构中一般只包含两个成员变量key和value,key代表剑指,value表示与key对应的信息。
比如:英汉互译的字典,每个字典中的英文单词与其中文含义保持一一对应的关系,就是key和value保持一一对应的关系。
SGI-STL中关于键值对的定义:
template
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中,元素的value也标识它(value就是key,类型为T),并且每个value必须是唯一的。
set中的元素不能在容器中修改(元素总是const),但是可以从容器中插入或删除它们。
在内部,set中的元素总是按照其内部比较对象(类型比较)所指示的特定严格弱排序准则进行
排序。
set容器通过key访问单个元素的速度通常比unordered_set容器慢,但它们允许根据顺序对
子集进行直接迭代。
set在底层是用二叉搜索树(红黑树)实现的。
注意:
与map/multimap不同,map/multimap中存储的是真正的键值对
value,但在底层实际存放的是由
set中插入元素时,只需要插入value即可,不需要构造键值对。
set中的元素不可以重复(因此可以使用set进行去重)。
使用set的迭代器遍历set中的元素,可以得到有序序列
set中的元素默认按照小于来比较
set中查找某个元素,时间复杂度为:O(logN)
set中的元素不允许修改(为什么?),因为修改之后就不满足AVL树或红黑树的条件了。
set中的底层使用二叉搜索树(AVL树或红黑树)来实现
#include
#include
using namespace std;
void TestSet()
{
/*用数组array中的元素构造set*/
int array[] = { 1, 3, 5, 7, 9, 2, 4, 6, 8, 0, 1, 3, 5, 7, 9, 2, 4,
6, 8, 0 };
int sz = sizeof(array) / sizeof(array[0]);
int i = 0;
printf(" 用数组array中的元素构造set\n");
for (i = 0; i < sz; i++)cout << array[i] << ' '; cout << endl;
set s(array, array + sizeof(array) / sizeof(array[0]));//迭代器(或原生指针)区间构造
printf("set中元素个数\n");
cout << s.size() << endl;
/* 正向打印set中的元素,从打印结果中可以看出:set可去重*/
printf(" 正向打印set中的元素,从打印结果中可以看出:set可去重\n");
for (auto& e : s)
cout << e << " ";
cout << endl;
/* 使用迭代器逆向打印set中的元素*/
printf(" 使用迭代器逆向打印set中的元素\n");
for (auto it = s.rbegin(); it != s.rend(); ++it)
cout << *it << " ";
cout << endl;
/* set中值为3的元素出现了几次*/
printf(" set中值为3的元素出现了几次\n");
cout << s.count(3) << endl;
}
int main()
{
TestSet();
return 0;
}
//一、统计次数
//法1.迭代器
map countMap;
for (auto& str : arr)
{
map::iterator it = countMap.find(str);
if (it != countMap.end())
{
//(*it).second++;
it->second++;//本质是it-> 先找到Node; 再(it->)->second;找到second;但编译器优化成了it->second;
//不能再写(it->)->second了
}
else
{
countMap.insert(make_pair(str, 1));
}
}
//法2.map的[]重载,以前是支持数组(顺序表)等连续的存储空间的结构使用。
// map的[]的意思变,返回key对应的value的引用。countMap[key]=value;
map countMap;
for (auto& str : arr)
{
// 1、str不在countMap中,插入pair(str, int()),然后在对返回value的引用,次数++
// 2、str在countMap中,返回value(次数)的引用,次数++;
countMap[str]++;
}
特点:
用例:
map dict;
dict.insert(make_pair("sort", "排序"));
dict["insert"];//如果不插入对应的value,则默认插入一个value()
dict["insert"] = "插入";
dict["left"] = "左边";
模拟实现:
mapped _type& operator[](const key_type& k)
{
//本质是return (*((this->insert(make_pair(k,mapped_type()))).first)).second;
return (*((insert(make_pair(k,mapped_type()))).first)).second;
}
//解读:
//一、insert解释
//single element (1) pair insert (const value_type& val);
// 简化为:pair insert(pair val);
//1. key已经在map中,插入失败,返回pair(key_iterator,false); 即返回pair(已经有的key的迭代器,false);
//2.key不在map中,插入成功,返回pair(new_key_iterator,true); 即返回pair(新的key的迭代器,true);
//二、return值的解释
//可拆分为
//pair ret=insert(make_pair(key,V()));插入成功返回的是新key的迭代器,插入失败返回的是已有key的迭代器。
//return ret.first ->second; ret是pair。ret.first找到pair的iterator,iterator也是pair类型的指针。再对(ret.first) ->second是找到迭代器的value。
//疑问:
//为什么不用find+insert实现。因为find要遍历一遍查找,insert又要遍历一遍插入。所以能单纯insert就单纯insert,只需要遍历一遍插入。
是map的一种,在头文件map中,要#include 包含头文件。
用例与multimap类似,区别在于pair
是map的一种,在头文件map中,要#include 包含头文件。
multimap mdict;
mdict.insert(make_pair("sort", "排序"));
mdict.insert(make_pair("left", "左边"));
mdict.insert(make_pair("left", "左边"));
mdict.insert(make_pair("left", "剩余"));
题目链接:692. 前K个高频单词 - 力扣(LeetCode)
题目描述:
法一 优先级队列+仿函数
struct Less//仿函数 stable_sort/sort传的less是排成升序,priority传的less是排成降序(大堆),注意区分。
//即,sort传的less,若前<后为真则不需要交换,使的前<后。priority传的less,若前<后为真则需要交换,使得前>后。
{
bool operator()(const pair& kv1,const pair&kv2)const
{
if(kv1.secondkv2.first)//若次数相同,则kv1的字母值大于kv2的字母值,则交换
return true; //把字母小的往前挪,字母大的往后挪
return false;
}
};
vector topKFrequent(vector& words, int k) {
map countMap;
//统计次数
for(auto& str:words)
{
countMap[str]++;//按字符大小顺序排一次序并且计数
}
//topk
//法1.push构造 优先级队列
// priority_queue,vector,Less>maxHeap;
// for(auto& kv:countMap)
// {
// maxHeap.push(kv);
// }
//法2.迭代器区间构造 优先级队列
typedef priority_queue,vector>,Less>MaxHeap;
MaxHeap mh(countMap.begin(),countMap.end());
//传Less优先级队列建大堆,即建起来之后是parent v;
while(k--)
{
v.push_back(mh.top().first);//取topk
mh.pop();
}
return v;
}
法二 默认排序sort+稳定性仿函数 / stable_sort稳定性排序 (相同的值相对顺序不变)
//stable_sort/sort无法对map进行排序,sort要求是联系物理空间的迭代器,所以先得把map的数据放到vector里。
struct Greater
{
bool operator()(const pair& kv1,const pair& kv2)
{
if(kv1.second>kv2.second)//若为真则不需要交换
return true;
if(kv1.second==kv2.second&&kv1.first topKFrequent(vector& words, int k) {
map countMap;
//统计次数
for(auto& str:words)
{
countMap[str]++;//按字符大小顺序排一次序并且计数
}
vector> sortV(countMap.begin(),countMap.end());
sort(sortV.begin(),sortV.end(),Greater());//也可以直接使用库里的stable_sort()进行排序,此时仿函数中可以不用考 //虑相同值的先后顺序,因为stable_sort()会默认进行处理。
vector v;
for(size_t i=0;i
法三 multimap
vector topKFrequent(vector& words, int k) {
map countMap;
//统计次数
for(auto& str:words)
{
countMap[str]++;//按字符大小顺序排一次序并且计数。
}
multimap> sortMap;//不能使用map,因为map会去重,可能会舍去很多出现次数相同的单词。
//multimap的greater和less是对于key来说的,也就是现在的int。按降序来排,和sort的less和greater含义相同,
//若 前>后 为真,则不交换。使得大的在前,小的在后。
//这里必须加个greater,因为如果不加的话排出来的是默认按less,这样排出来的multimap sortV是升序的。
//要取次数topk大只能用反向迭代器在sortV里倒着去取,但是因为sortV里的字母的顺序是已经在前面计数+字符大小排序时排好的 //了,倒着去取的话会使字母的顺序逆序。是不行的。
for(auto& kv:countMap)
{
sortMap.insert(make_pair(kv.second,kv.first));//再按出现次数插入multimap排一次序,按出现次数排降序。
}
vector v;
multimap>::iterator it=sortMap.begin();
for(size_t i=0;isecond);//取topk
it++;
}
return v;
}
题目链接:https://leetcode.cn/problems/intersection-of-two-arrays/submissions/
题目描述:
法一 不相同,则小的++;相等,则同时++,并插入vector。最后的vector即为交集。
vector intersection(vector& nums1, vector& nums2) {
set s1(nums1.begin(),nums1.end());//默认构造set里已经排好序,升序。
set s2(nums2.begin(),nums2.end());
auto it1=s1.begin();
auto it2=s2.begin();
vector v;
while(it1!=s1.end()&&it2!=s2.end())
{
//让小的++,因为小的一定不属于交集。
if(*it1<*it2)//不相等的则为差集
{
it1++;
}
else if(*it2<*it1)
{
it2++;
}
else//找到相等才是交集。
{
v.push_back(*it1);
it1++;
it2++;
}
}
return v;
}