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

关联式容器

序列式容器:如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);
}


  • 2
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
C++ STL中没有直接提供二叉搜索树的实现,但STL中有一些关于树的容器,比如setmap,它们底层的实现就是基于红黑树(一种平衡二叉搜索树)的。你可以使用这些容器来实现二叉搜索树的功能。关于二叉搜索树的一些知识,比如二叉树的遍历、迭代、线索二叉树、堆、Huffman编码、AVL树等都可以在STL中找到相应的实现。 二叉搜索树查找可以通过比较根节点的值和目标值的大小来判断是往左子树还是往右子树查找,并重复这个过程直到找到目标值或者遍历到叶子节点为止。常规实现使用循环来实现查找,递归实现使用递归函数来查找二叉搜索树的插入操作也可以通过递归或循环来实现,根据目标值和当前节点的值的大小关系来决定是往左子树还是往右子树插入新节点。 STL中的二叉搜索树容器如setmap提供了插入、删除和查找等功能,并且保持了二叉搜索树的性质。你可以使用这些容器来处理二叉搜索树相关的操作。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [C++ STL 数据结构 树](https://download.csdn.net/download/xinxipan/3008948)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [【C++ STL】-- 二叉搜索树](https://blog.csdn.net/weixin_64609308/article/details/128018280)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v93^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小白在进击

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值