当前位置: 首页 > news >正文

C++:set和map详解版

目录

set

1.set类的介绍

2.set迭代器

3.set构造

4.set增删查

增insert

插入单个key

迭代器区间插入

支持列表插入

查find

删除erase

删除某个位置

给个值给他删

支持迭代器区间删除  [首元素,尾元素)

count,为mulset准备的

lower_bound

upper_bound

multiset:只是与set有略微的差异

两个题目

map

在插入之前先介绍一下返回值pair类型是啥

插入insert

1.有名pair对象插入

2.匿名的pair插入

3.make_pair函数模板插入,返回一个构造的pair对象

4.C++11,支持多参数隐式类型转换,用{}包裹的多个值,通过构造函数隐式转换成类对象

遍历

支持列表初始化

以下方式可以统计统计水果次数

mutimap

相关的两个题


序列式容器:保证插入数据的顺序性,逻辑结构是线性的,如vector,list,两个位置的一般没有紧密联系,位置交换后仍然是序列式容器

关联式容器:逻辑结构是非线性的,两个位置之间有紧密联系,交换一下,它的存储结构就被破坏。比如map/set,unordered_map/unordered_set.

本篇文章围绕map/set来讲解,底层是红黑树,红黑树是平衡二叉搜索树。map是key的搜索场景的结构,set对应key/value的搜索场景。


set

set是平衡二叉搜索树,增删查的效率是高度次logN,迭代器遍历走中序,所以是有序的

set容器中,每个键值key必须唯一,且并不支持修改(会破坏底层结构),但是可以进行插入,移除

1.set类的介绍

template < class T,                        // set::key_type/value_typeclass Compare = less<T>,        // set::key_compare/value_compareclass Alloc = allocator<T>      // set::allocator_type> class set;

T是key的类型,一般我们不需要传后两个模板参数,一个是仿函数(默认是key小于比较),一个是空间配置器

这里key的类型T写两个key_type,value_type是为了和后面的map匹配

重点介绍:

2.set迭代器

是一个双向迭代器,支持++,--

3.set构造

无参的默认构造

explicit set (const key_compare& comp = key_compare(),const allocator_type& alloc = allocator_type());

迭代器区间构造

 set (InputIterator first, InputIterator last,const key_compare& comp = key_compare(),const allocator_type& = allocator_type());

拷贝构造

set (const set& x);

初始化列表构造

set (initializer_list<value_type> il,const key_compare& comp = key_compare(),const allocator_type& alloc = allocator_type());

4.set增删查

增insert

由于set的键值唯一性,插入前需要判断是否这个元素是唯一的,如果不是,不插入该元素,并返回指向已有元素的迭代器(如果函数有返回值的话)

插入单个key
pair<iterator,bool> insert (const value_type& val);

返回的是一个pair类型对象,它包含两个成员,一个是插入元素的迭代器,另一个是否插入成功

先忽略返回值pair,等下再详细讲解

template <class T1, class T2>
struct pair {T1 first;  // 第一个元素T2 second; // 第二个元素
};//大致这样
int main()
{// 去重+升序排序set<int> s;// 去重+降序排序(给⼀个⼤于的仿函数,降序)//set<int, greater<int>> s;//1.插入单个值s.insert(5);s.insert(2);s.insert(7);s.insert(5);//相同的值5插入失败auto it = s.begin();while (it != s.end()){//*it = 1;//不支持修改cout << *it << " ";++it;}cout << endl;return 0;
}
迭代器区间插入
template <class InputIterator>
  void insert (InputIterator first, InputIterator last);

InputIterator可以是一个模板参数,代表一种迭代器类型------不限制具体是哪种容器的迭代器

set<int> s = { 5 };
vector<int> x = { 1,2,3,4 };
//迭代器区间插入
s.insert(x.begin(), x.end());
for (auto e : s)
{cout << e << " ";
}
cout << endl;
支持列表插入
void insert (initializer_list<value_type> il);
//2.插入一段列表值
s.insert({ 1,2,3,4,2 });

查find

iterator find (const value_type& val);

如果找到与val值相等的元素,则返回指向该元素的迭代器,否则,返回end()

set中的find表示遍历这棵树,最多查找高度次O(logN)

而算法库中的find是一个个比较,O(n)

删除erase

删除某个位置
iterator erase (const_iterator position);
// 删除最⼩值s.erase(s.begin());
给个值给他删
size_type erase (const value_type& val);
int num = s.erase(0);
if (num == 0)
{cout <<  "不存在!" << endl;
}
for (auto e : s)
{cout << e << " ";
}
cout << endl;

通过返回值判断是否删除成功/失败,返回删除了几个数0/1,不用bool的原因是为了兼容muliset(可能删除>=0个相同的数)

可以认为这个erase底层就是一个find+迭代器,有就删,没找到就不动

 auto it = s.find(0);if (it != s.end())//如果没找到,find会返回end(){s.erase(it);}else//没找到就不动{cout << " 不存在! " << endl;}for (auto e : s){cout << e << " ";}cout << endl;

erase后迭代器it失效吗?

想想二叉搜索树,

1.删除叶子节点/只有一个孩子的节点

则直接删除这个节点,it被释放,成为野指针,不能被访问,即迭代器it失效

2.删除的节点有两个孩子

拿其他节点的值替换待删除结点的值,也认为它失效,因为it指向的迭代器即使能访问,访问替换后的节点,但它的意义已经变了。也认为迭代器失效。

支持迭代器区间删除  [首元素,尾元素)
iterator erase (const_iterator first, const_iterator last);

count,为mulset准备的

在容器中搜索与 key 相等的元素,并返回匹配的数量。0/1

判断key在不在比较方便

  if (s.count(2)){cout << 2 << "在" << endl;}

lower_bound

在有序的set中,定位第一个值>=key的元素,并返回指向这个元素的迭代器

iterator lower_bound (const value_type& val) const;

upper_bound

定位第一个值>key的元素,并返回指向这个元素的迭代器

iterator upper_bound (const value_type& val) const;

比如要把一段区间删除[30,50],10,20,30,40,50,60,70,80,90

用erase迭代器区间删除的话,需要类似这样 erase(30的迭代器,50的下一个迭代器)

int main()
{std::set<int> myset;for (int i = 1; i < 10; i++)myset.insert(i * 10); // 10 20 30 40 50 60 70 80 90for (auto e : myset){cout << e << " ";}cout << endl;//迭代器区间删除[30,50]--[30,60)auto itlow = myset.lower_bound(30);//返回>=30的位置 auto itup = myset.upper_bound(50);//返回>50的位置myset.erase(itlow, itup);for (auto e : myset){cout << e << " ";}cout << endl;//10,20,60,70,80,90return 0;
}

当然也是按照搜索树的规则去找

multiset:只是与set有略微的差异

multiset相比于set不会去重,即允许键值冗余

与set的差异体现在:

find:好多个x,找中序的第一个x ,++后还能的得到相同的x,即能找到所有的x

find找中序的第一个--左子树的第一个,右子树不用管、找到3继续向左子树找3,知道左子树找不到,此节点就是中序第一个

int x;
cin >> x;
auto pos = s.find(x);
while (pos != s.end() && *pos == x)
{cout << *pos << " ";++pos;
}
cout << endl;

insert:相等的key插到右面还是左边都一样,最后都会旋转到左边

erase:可以删除多个相同的key

删除迭代器 pos 指向的元素后,通过 erase 的返回值获取 “下一个有效迭代器”

int x;
cin >> x;
auto pos = s.find(x);
while (pos != s.end() && *pos == x)
{pos = s.erase(pos);
}
cout << endl;
it = s.begin();
while (it != s.end())
{cout << *it << " ";++it;
}
cout << endl;

s.erase(2);会全部删除2

count:用来统计相同key的个数0/>0

两个题目

1.环形链表

2.交集


map

template < class Key,                                     // map::key_typeclass T,                                       // map::mapped_typeclass Compare = less<Key>,                     // map::key_compareclass Alloc = allocator<pair<const Key,T> >    // map::allocator_type> class map;

key_type是k关键字key的类型,mapped_type是值value的类型,只按照key来比较,默认升序,与value无关;

支持增删查改

改:支持修改value,不支持改key

在插入之前先介绍一下返回值pair类型是啥

value_type是一个pair类型,pair是一个类模板

template<class Key,class T>
class mymap
{//对pair<const Key, T>起别名value_typetypedef pair<const Key, T> value_type;//mymap里面定义了一个模板,参数为T1,T2,对应pair声明中的Key,Ttemplate<class T1, class T2>struct pair{typedef T1 first_type;typedef T2 second_type;T1 first;//keyT2 second;//value};
};

pair就是把key,value封装,多封了一层

插入insert

pair<iterator,bool> insert (const value_type& val);

返回了pair<iterator,bool>,包含两个成员变量iterator first,bool second

1.有名pair对象插入

//1有名
map<string, string> dict;
pair<string, string> v1("win", "赢");
dict.insert(v1);

2.匿名的pair插入

//2匿名
dict.insert(pair<string, string>("success", "成功"));

3.make_pair函数模板插入,返回一个构造的pair对象

make_pair

//3make_pair函数模板
dict.insert(make_pair("sort", "排序"));

用函数模板构造一个pair进行返回

4.C++11,支持多参数隐式类型转换,用{}包裹的多个值,通过构造函数隐式转换成类对象

void insert (initializer_list<value_type> il);
//4多参数隐式类型转换
dict.insert({ "apple", "苹果" });

遍历

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

不可以这样写,对迭代器进行解引用返回的是value_type,是pair对象,而std::cout不支持输出pair类型,即不支持同时返回两个值

咋遍历,打印数据不能*it,先对迭代器解引用,通过结点指针再去取节点里面的数据,key和value

1.解引用+.

 while (it != dict.end()){cout << (*it).first << " " << (*it).second << endl;++it;}

2.迭代器中重载了operator->

// 模拟迭代器的简化实现
template <class T>
struct iterator {T* ptr;  // 重载 operator->T* operator->() const{return ptr; //返回指向元素的指针 }
};

因此能支持->访问

while (it != dict.end())
{cout << it->first << " " << it->second << endl;++it;
}
cout << endl;

本质上是两个->,第一个是运算符重载返回的指针,第二个是对指针进行解引用

while (it != dict.end())
{cout << it.operator->()->first << " " << it.operator->()->second << endl;++it;
}
cout << endl;

如果插入时,key相等,但value不相等,会不会更新value?不会

可以修改value,不支持改key

 map<string, string>::iterator it = dict.begin();while (it != dict.end()){it->first += 'x';//errorit->second += 'x';++it;}

支持列表初始化

map<string, string> dict = { {"win", "赢"},{"success", "成功"},{"apple", "苹果"} };

还可以这样,不写有名,匿名,直接走隐式类型转换pair<string,string>

erase只跟key有关,insert根key/value有关,find只跟key有关

以下方式可以统计统计水果次数

map<string int>

苹果:6

string arr[] = { "苹果", "西⽠", "苹果", "西⽠", "苹果", "苹果", "西⽠" };
map<string, int> countMap;
for (const auto& str : arr)
{auto ret = countMap.find(str);if (ret == countMap.end()){countMap.insert({ str, 1 });}else{ret->second++;}
}
for (const auto& e : countMap)
{cout << e.first << ":" << e.second << endl;
}
cout << endl;

2.还有另一种方法,直接借助【】统计,count[str]++

(*((this->insert(make_pair(k,mapped_type()))).first)).second

即我给map里的key,返回对应的value,然后对value进行修改

功能:插入+查找+修改

key不存在,即插入,返回映射值的引用

插入失败也充当了查找的功能,方便给【】使用

operaotr的内部实现,通过insert实现

mapped_type& operator[](const key_type& k)
{pair<iterator, bool> ret = insert({ k, mapped_type() });iterator it = ret.first;return it->second;
}

两个pair,底层+返回值,是个类模板

count[str]++,咋统计次数的,pair ret ; iterator it=ret.first ; it->second++

总结:

  • str不存在,插入+修改
  • str存在时,查找+修改(++次数
  • 查找的前提是得确定 key 在,否则就是插入了
map<string, string> dict;
dict.insert(make_pair("apple", "苹果"));//key不存在时,插入功能{"orange",string()}
dict["orange"];//key不存在,插入+修改
dict["banana"] = "香蕉";//key存在,查找+修改
dict["apple"] = "好吃的苹果";//查找,确定key在
cout << dict["apple"] << endl;//插入
cout << dict["grape"] << endl;

mutimap

插入一定成功

erase连续删除key,中序第一个开始

不支持[],因为不知道返回哪个value

相关的两个题

1.链表的复制

2.前k、个高频单词

http://www.dtcms.com/a/470442.html

相关文章:

  • 【Docker】docker存储配置与管理
  • 网站设计结果怎么做电视台网站
  • 廉江网站制作制作静态网站制作
  • 纯知识干货vue2学习之问答六
  • dnf做心悦宠物的网站官方模板关键字生成的代码添加在网站的什么地方?
  • 主流视频各种压缩码对比
  • 如何确定网站栏目静态网站开发工具有哪些
  • commons-collections4
  • (13)100天python从入门到拿捏《目录操作》
  • SRC漏洞挖掘
  • Kettle作业并行设置及多前置任务同时运行完成才执行后续节点的方案
  • 如何给网站做301重定向网站集约化建设通知
  • PS基本教学(一)——认识Photoshop软件
  • Java中数据驱动测试的多接口关联处理
  • 用 TypeScript进行Hardhat测试
  • 淘宝网站建设那么便宜营销型网站开发 语言选择
  • ps做网站尺寸多少像素深圳网站建设团队
  • 做私房蛋糕在哪些网站写东西wordpress 文章 pin
  • 【ProtoBuf】快速上手
  • DAC1282寄存器介绍以及模式操作介绍
  • winlogon!SignalManagerResetSignal函数分析之循环的次数和信号管理数组的->20h有关
  • 从fat看文件系统的加载流程走读
  • patchmatch翻译总结
  • 绍兴免费自助建站wordpress 屏蔽ip
  • TCP 网络编程笔记:TcpListener、TcpClient 与核心用法
  • 群晖的网站开发搜索百度一下
  • 【词根】2025-10-11词根学习
  • 【MFC】项目源码过大,不影响项目设置的打包办法
  • 做聚美优品网站得多少钱wordpress 知更鸟 公告
  • 网站的中英文翻译是怎么做的公司手机网站建设价格