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

【C++】二叉搜索树set/map

文章目录

    • 关联式容器
    • 键值对
    • key模型搜索平衡二叉树
    • set的基本使用
      • set的插入(insert)
      • set的查找(find)
      • set的删除(erase)
      • set的遍历
      • multiset使用
    • key/val模型搜索二叉树
    • map的基本使用
      • map的插入(insert)
      • map的删除(erase)
      • map的查找(find)
      • map的遍历
      • operator[] (重点)

关联式容器

序列式容器:如vector,list等,这些容器底层为线性数据结构,其存储的就是元素本身
关联式容器:map/set,与序列式容器不同,其通过键值对<key, val>来存储数据,其检索效率更高

键值对

在C++中使用pair结构体存储键值对,其有两个模板参数。其中存储了两个成员变量first(key)和second(val)。我们可以通过make_pair()函数创建键值对
在这里插入图片描述

key模型搜索平衡二叉树

STL中的set就是以key为模型创建的搜索平衡二叉树
在这里插入图片描述
在STL中有两种set,普通的set的key不允许两个节点出现相同的key,但multiset允许。
普通的set可以实现查重,搜索查询的功能。
在set中key就是val,val就是key

set的基本使用

set定义的类型
在这里插入图片描述

set的插入(insert)

在这里插入图片描述
第一种插入方法
形参为一个const 修饰的val,很好理解就是插入一个值为val的节点但是返回值是一个pair
,其第一个参数类型是个迭代器,第二个参数是个bool类型的。第一个类型返回的就是值为val节点的迭代器。bool表示是否插入成功,若插入失败返回false,成功返回true

void test1()
{
  set<int> s;
  s.insert(1);
  pair<set<int>::iterator, bool> ret1 = s.insert(1); //已经有1,不支持重复,插入失败
  pair<set<int>::iterator, bool> ret2 = s.insert(2);//没有2,插入2,成功
  cout << ret1.second << endl;
  cout << ret2.second << endl;
}
[clx@VM-20-6-centos test]$ ./test
0
1

第二种插入方法(不常用):
给一个迭代器,在迭代器处放入值为val的节点

可以对数据进行排序,删重

set的查找(find)

在这里插入图片描述
set的find函数很简单,输入一个key返回其的迭代器。若没找到则返回 set.end()

set的删除(erase)

在这里插入图片描述
第一种方法:使用迭代器删除(和find结合使用)
第二种方法:直接使用key删除
第三种方法:给一个迭代器区间,对其中数据删除

void test2()      
{      
  set<int> s;      
  s.insert(1);      
  pair<set<int>::iterator, bool> ret1 = s.insert(1);      
  pair<set<int>::iterator, bool> ret2 = s.insert(2);    
  cout << s.erase(1) << endl;    
  cout << s.erase(3) << endl;    
}                         
[clx@VM-20-6-centos test]$ ./test
1            //删除成功返回1
0            //删除失败返回0

1.若使用第一种方法删除,传入的迭代器无效,则编译器会报错
2.第二种方法会删除此树中所有值为key的节点,在set中若有那节点数量必定为1,所以返回1,但是在multiset中可能会有多个,erase会将这些节点全部删除,并返回删除节点数量

void set_test()
{
  set<int> s;
  //insert 用法
  //insert 可以排序加去重
  s.insert(3);
  s.insert(5);
  s.insert(2);
  s.insert(4);
  s.insert(1);
  s.insert(6);
  //erase 用法
  //1.key 2.iterator 3.iterator range
  set_print(s);
  //方法2
  s.erase(2);                    //删除成功返回1,失败返回0 返回值的类型是 unsigned int
  set_print(s);
  //方法1
  s.erase(s.find(4));            //find返回一个迭代器
  set_print(s);
  //方法3
  s.erase(s.find(1), s.find(3)); //find range
  set_print(s);
  //s.erase(s.find(100));//若使用迭代器必须要是有效的,无效会报错
}

set的遍历

void set_test()
{
  set<int> s;
  //insert 用法
  //insert 可以排序加去重
  s.insert(3);
  s.insert(5);
  s.insert(2);
  s.insert(4);
  s.insert(1);
  s.insert(6);
  //method 1 : iterator(迭代器)
  set<int>::iterator it = s.begin();
  while (it != s.end())
  {
    cout << *it << " ";
    it++;
  }
  cout << endl;
  //method 2 : range for(范围for)
  for (auto& e : s)
  {
    cout << e << " ";
  }
  cout << endl;
 

multiset使用

void multiset_test()
{
  //multiset 不去重 可排序
  multiset<int> ms;
  ms.insert(5);
  ms.insert(3);
  ms.insert(2);
  ms.insert(6);
  ms.insert(1);
  ms.insert(4);
  ms.insert(5);
  for (auto& e : ms)
  {
    cout << e << " ";
  }
  cout << endl;
  //cout << ms.erase(5) << endl;            //返回值是5在ms中出现的次数
  multiset<int>::iterator pos = ms.find(5); //find 的 val有多个值时, 返回的是中序第一个val的结点的迭代器
  while (pos != ms.end())
  {
    cout <<  *pos << " ";    
    pos++;    
  }
  cout << endl;    
  //方法1    
  //删除所有key为5的结点                                                                                                                                                                
  //auto start = pos;    
  //auto end = pos;    
  //while (pos != ms.end() && *pos == 5)    
  //{    
  //  end = pos;    
  //  pos++;    
  //}    
  //ms.erase(start, end);    
  //方法2    
  //while (pos != ms.end() && *pos == 5)    
  //{    
  //  //必须要记录下一个结点的位置,不然erase后pos就变成野指针,无法++    
  //  auto next = pos;    
  //  next++;    
  //  ms.erase(pos);    
  //  pos = next;
  //}
}

因为使用和set相近,就不重新描述了,部分需要注意的点都写在代码中了

key/val模型搜索二叉树

STL中map就是key/val模型的搜索二叉树,其通过pair(键值对)来存储数据,并通过key来对数据进行排列,便于检索

map的基本使用

map的成员类型
在这里插入图片描述

map的插入(insert)

在这里插入图片描述

其中最常用的方法是1,接下来我们将用三种形式进行插入数据

void map_test1()    
  {                                     
    map<string, string> dict;    
    //方法1:创建一个键值对pair,然后调用insert
    pair<string, string> kv1("sort", "排序");                                                                                                                                           
    dict.insert(kv1);    
    //方法2:创建一个临时对象
    dict.insert(pair<string, string>("left", "左"));
    //方法3:调用make_pair函数    
    dict.insert(make_pair("right", "右"));  
  }  

map的删除(erase)

在这里插入图片描述

和set相同,这里就不赘述了

map的查找(find)

在这里插入图片描述

map的遍历

 void map_test1()    
  {                                     
    map<string, string> dict;    
    pair<string, string> kv1("sort", "排序");                                                                                                                                           
    dict.insert(kv1);    
    dict.insert(pair<string, string>("left", "左"));    
    dict.insert(make_pair("right", "右"));    
    //使用迭代器调用
    map<string, string>::iterator it = dict.begin();    
    while (it != dict.end())    
    {                                                                                                                  
      cout << it->first << ":" << it->second << endl;     
      it++;                          
    }    
    cout << endl;
    //使用范围for调用             
    for (auto& kv : dict)    
    {    
      cout << kv.first << ":" << kv.second << endl;    
    }    
    cout << endl;                                                                    
  }                        

operator[] (重点)

在这里插入图片描述
重载[ ]操作符是map非常特殊的地方,和其他容器不同其方括号并非返回其中数据。其返回值非常特殊,是pair.second的引用。
若key == k的结点存在,返回此节点value的引用
若key == k的结点不存在,创建key == k的结点,并将此节点的value初始化为mapped_type(),并返回此结点val的引用
operatorp[]的功能 1.插入key == k的结点 2.查找key == k的结点的val 3.修改key == k的结点的val
本质:

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

接下来我们将通过一道题来解释operator[]的用法
题目:统计不同水果出现的次数
方法1:


void map_test2()
{
    string arr[] = {"苹果" , "香蕉", "菠萝", "桃子", "苹果", "菠萝", "苹果"};
    map<string, int> count_map;
    
    //方法一:find + insert                                                                                                                                                              
    for (auto fruit : arr)      
    {                      
      auto ret = count_map.find(fruit);  //若不存在fruit则返回count_map.end()  
      if (ret == count_map.end())      
      {                    
        count_map.insert(make_pair(fruit, 1));  //插入节点    
      }                    
      else                 
      {                    
        ret->second++;               //已存在ret迭代器的second次数加一
      }                    
    }                      
}

方法2:

for (auto fruit : arr)                                                                                                                                                              
    { 
      auto ret = count_map.insert(make_pair(fruit, 1)); 
      //直接使用insert的返回值pair<iterator, bool>
      if (ret.second == false)  //若已经存在
      { 
        (ret.first)->second++; //ret.first 得到目标节点迭代器  
      }                    
    }                     

方法三:

 //方法三:operator[]
  for (auto& fruit : arr)
  {
    count_map[fruit]++;
  }

  //operaotp[]解释
  //mapped_type& operator[] (const key_type& k)
  //{ return (this->insert(make_pair(k, mapped_type())).first->second); }
  //mapped_type()就是临时对象,也就是调用默认构造出来的mapped_type类型的临时对象
  //若key==k的结点存在,返回此节点value的引用
  //若key==k的结点不存在,创建key==k的结点,并将此节点的value初始化为mapped_type(),并返回此结点val的引用
  //operatorp[]的功能 1.插入key==k的结点 2.查找key==k的结点的val 3.修改key==k的结点的val
  count_map["樱桃"];                                    //插入
  count_map["樱桃"] = 3;                                //修改  等价与 count_map.insert(makepair("樱桃", 3)); 插入 + 修改
  cout << "樱桃" << ":" << count_map["樱桃"] << endl;   //查找
  for (auto& e : count_map)
  {
    cout << e.first << ":" << e.second << endl; 
  }
  cout << endl;
  //multimap 没有operatorp[], 与 map的区别就是不管key在不在都插入,允许冗余
  //count(key_type k)函数可以帮助multimap查找有多少个相同key的结点
                                                                                            

进阶:
输出喜欢人数最多的三种水果

struct Compare 
{
  bool operator()(const map<string, int>::iterator m1, const map<string, int>::iterator m2)
  {
    return m1->second > m2->second;
  }
};

struct Compare_priority_max
{
  bool operator()(map<string, int>::iterator l, map<string, int>::iterator r)
  {
    //小于建大堆,大于建小堆
    return l->second < r->second;
  }

};

void GetFavoriteFruit(const vector<string>& fruits, size_t k)                                                                                                                           
{
  //仿函数,用于方法一sort 的 Compare条件
  Compare comp;
  map<string, int> count_map;
  for (auto str : fruits)
  {
    count_map[str]++;
  }
  //方法一:将map的迭代器放入vector进行快排
  //vector<map<string, int>::iterator> v;
  //map<string, int>::iterator it = count_map.begin();
  //while (it != count_map.end())
  //{
  //  v.push_back(it);
  //  it++;
  //}
  //sort(v.begin(), v.end(), comp);
  //for (size_t i = 0; i < k; i++)
  //{
  //  cout << v[i]->first << ":" << v[i]->second << endl;
  //}
  //cout << endl;

  //方法二:使用搜索树
  //multimap<int, string> max_tree;
  //for (auto fruit : count_map)
  //{
  //  max_tree.insert(make_pair(fruit.second, fruit.first));
  //}
  //auto max = max_tree.rbegin();
  //for (size_t i = 0; i < k; i++)
  //{
  //  cout << max->second << ":" << max->first << endl;
  //  max++;
  //}
  //cout << endl;
  //

  //方法三:使用优先级队列
  priority_queue<map<string, int>::iterator, vector<map<string,int>::iterator>, Compare_priority_max> sort_queue;
  map<string, int>::iterator it = count_map.begin();
  while (it != count_map.end())
  {
    sort_queue.push(it);
    it++;
  }
  for (size_t i = 0; i < k; i++)
  {
    cout << sort_queue.top()->first << ":" << sort_queue.top()->second << endl;
    sort_queue.pop();
  }
  cout << endl;
  
}
void map_test3()
{
  vector<string> arr = {"苹果" , "香蕉", "菠萝", "桃子", "苹果", "菠萝", "苹果", "樱桃", "苹果", "香蕉"};
  GetFavoriteFruit(arr, 3);
}


相关文章:

  • 最短路径查找Dijkstra算法
  • [数字媒体] Photoshop基础之图像校正、抠图(证件照)和融合
  • 【毕业设计】基于的单片机的移动硬盘设计与实现 - stm32 嵌入式 物联网
  • 使用Python的requests库发送SOAP请求,错误码415
  • Python爬虫技术系列-02HTML解析-lxml+BS4
  • 今日头条——机器学习算法岗1234面
  • 【笔记】快速理解傅里叶级数
  • 宣布发布 .NET 7 Release Candidate 1
  • 8万Star,这个开源项目有点强
  • 数据批处理速度慢?不妨试试这个
  • 透过安全事件剖析黑客组织攻击技术(2FA/MA的攻击手法)
  • java毕业设计——基于Java+AI的五子棋游戏设计与实现(毕业论文+程序源码)——五子棋游戏
  • 29、Java 中的接口详解
  • mysql中怎么防止数据丢失
  • 软件开发中会使用到的图
  • 分享的文章《人生如棋》
  • [数据结构]链表的实现在PHP中
  • Brief introduction of how to 'Call, Apply and Bind'
  • CEF与代理
  • java B2B2C 源码多租户电子商城系统-Kafka基本使用介绍
  • Linux后台研发超实用命令总结
  • orm2 中文文档 3.1 模型属性
  • Spring Cloud中负载均衡器概览
  • Zsh 开发指南(第十四篇 文件读写)
  • 得到一个数组中任意X个元素的所有组合 即C(n,m)
  • 二维平面内的碰撞检测【一】
  • 给自己的博客网站加上酷炫的初音未来音乐游戏?
  • 前端面试之闭包
  • 如何利用MongoDB打造TOP榜小程序
  • 设计模式(12)迭代器模式(讲解+应用)
  • 体验javascript之美-第五课 匿名函数自执行和闭包是一回事儿吗?
  • 小程序01:wepy框架整合iview webapp UI
  • ​520就是要宠粉,你的心头书我买单
  • ​第20课 在Android Native开发中加入新的C++类
  • # 20155222 2016-2017-2 《Java程序设计》第5周学习总结
  • # Maven错误Error executing Maven
  • ###C语言程序设计-----C语言学习(3)#
  • #vue3 实现前端下载excel文件模板功能
  • (二)学习JVM —— 垃圾回收机制
  • (附源码)ssm考生评分系统 毕业设计 071114
  • (三)Hyperledger Fabric 1.1安装部署-chaincode测试
  • (心得)获取一个数二进制序列中所有的偶数位和奇数位, 分别输出二进制序列。
  • (原創) 未来三学期想要修的课 (日記)
  • (转)关于pipe()的详细解析
  • .form文件_SSM框架文件上传篇
  • .Net CF下精确的计时器
  • .net core使用ef 6
  • .NET Core中Emit的使用
  • .Net Winform开发笔记(一)
  • .Net 高效开发之不可错过的实用工具
  • .NET 中让 Task 支持带超时的异步等待
  • :如何用SQL脚本保存存储过程返回的结果集
  • @Mapper作用
  • @media screen 针对不同移动设备
  • @Service注解让spring找到你的Service bean