c++ stl set

虽然经常使用c++的stl,set也是经常使用的一个容器,但是发现对于set的理解还是比较浅显。今天使用时无意就撞入了一个坑里。

stl中set和map都是用红黑树实现的。set是key和value相同的结构。

代码如下:

   1: #include <set>
   2: #include <iostream>
   3: #include <iterator>
   4: #include <string>
   5: using namespace std;
   6:  
   7: typedef struct Object {
   8:   Object(int t, string k) : timestamp(t), key(k) {
   9:   }
  10:   int timestamp;
  11:   string key;
  12:   bool operator <(const Object& o) const {
  13:     return key.compare(o.key);
  14:   }
  15: }Object;
  16:  
  17: ostream& operator<<(ostream& os, const Object& obj) {
  18:   os << "(" << obj.timestamp << ":" << obj.key << ")";
  19: }
  20:  
  21: bool Compare(const string& s1, const string& s2) {
  22:   return s1.compare(s2);
  23: }
  24: int main(int argc, char** argv) {
  25:   {
  26:     set<Object> obj_set;
  27:     cout << "insert (1, key1) : " << obj_set.insert(Object(1, "key1")).second << endl;
  28:     cout << "insert (2, key1) : " << obj_set.insert(Object(2, "key1")).second << endl;
  29:     cout << "insert (5, key5) : " << obj_set.insert(Object(5, "key5")).second << endl;
  30:     cout << "insert (3, key3) : " << obj_set.insert(Object(3, "key3")).second << endl;
  31:     cout << "insert (4, key7) : " << obj_set.insert(Object(4, "key7")).second << endl;
  32:     cout << "insert (4, key6) : " << obj_set.insert(Object(4, "key6")).second << endl;
  33:     cout << "insert (4, key9) : " << obj_set.insert(Object(4, "key9")).second << endl;
  34:     cout << "insert (4, key8) : " << obj_set.insert(Object(4, "key8")).second << endl;
  35:     copy(obj_set.begin(), obj_set.end(), std::ostream_iterator<Object>(std::cout, " "));
  36:     cout << endl;
  37:     cout << "obj_set.find(Object(1, key1)) : " << (obj_set.find(Object(1, "key1")) != obj_set.end()) << endl;
  38:     cout << "obj_set.find(Object(1, key1)) : " << (obj_set.find(Object(1, "key1")) != obj_set.end()) << endl;
  39:     cout << "obj_set.find(Object(3, key3)) : " << (obj_set.find(Object(3, "key3")) != obj_set.end()) << endl;
  40:     cout << "obj_set.find(Object(2, key4)) : " << (obj_set.find(Object(2, "key4")) != obj_set.end()) << endl;
  41:   }
  42:   return 0;
  43: }

执行结果如下:

image

    执行结果中两个问题:

    第一:输出的set的顺序不对。

    第二:find结果不对。 3,key3 是存在的而find为false

    仔细看了set的说明才弄明白。参见后面构造函数部分

set的构造函数:

template < class T,                        // set::key_type/value_type
           class Compare = less<T>,        // set::key_compare/value_compare
           class Alloc = allocator<T> >    // set::allocator_type
           > class set;
从set的构造函数看出,其需要一个比较函数,默认使用的less比较函数。
通常set中存放一些int、string等类型的数据时,使用默认的通常不会有什么问题。
而如果是我们自定义的一些结构体的时候,则需要为其提供比较函数的实现。
比较函数实现有两种范式:
一:该结构体是你自己设计的,则那么在类中重载operator<就可以了,需要注意的就是必须将其设计为const成员函数
    如上面为:bool operator <(const Object& o) const; 
    注意必须为const成员函数
二: 该结构体是别人设计的,类中没有重载operator<,而且你也没有办法去修改别人的代码。那么你就需要再外部设计一个比较器。
   1: class ObjCompare {
   2:   public:
   3:     bool operator()(const Object& o1, const Object& o2) {
   4:       return o1.key.compare(o2.key);
   5:     }   
   6: };
set 初始化为:set obj_set; 别的相同
同时需要注意:该函数的要求: 向set中添加的元素类型必须重载<操作符用来排序。排序满足以下准则:
1、非对称,若A<B为真,则B<A为假。
2、可传递,若A<B,B<C,则A<C。
3、A<A永远为假。
set中判断元素是否相等:
if(!(A<B || B<A)),当A<B和B<A都为假时,它们相等。
而注意上面我们使用的为string的compare函数,而我们看下该函数的声明:

string (1)

int compare (const string& str) const;

substrings (2)

int compare (size_t pos, size_t len, const string& str) const;
int compare (size_t pos, size_t len, const string& str,
             size_t subpos, size_t sublen) const;

c-string (3)

int compare (const char* s) const;
int compare (size_t pos, size_t len, const char* s) const;

buffer (4)

int compare (size_t pos, size_t len, const char* s, size_t n) const;
注意函数的返回值为int,
image
这时就明白了,上面我们实现的比较函数。当二者相等时返回0,为false, 而当不等时,则返回值或者为1或者为-1,都是true,是不满足set的排序函数的要求的。
我们修改下比较函数为:
image
执行结果为:
image
此时可以看出set是按照key有序的。同时find的结果也是正确的。
(注意[2,key1] 的find结果为1是正确的,因为我们的比较函数是针对key的,不对timestamp比较)

insert方法

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

该方法返回的是一个pair,可以使用insert(xxx).second 来判断插入是否成功

with hint (2)  iterator insert (iterator position, const value_type& val);

查看函数说明:

The function optimizes its insertion time if position points to the element that will precede the inserted element.

Notice that this is just a hint and does not force the new element to be inserted at that position within theset container (the elements in a set always follow a specific order).
Member types iterator and const_iterator are defined in map as a bidirectional iterator type that point to elements.

即:如果提供的position是带插入元素之前的节点,那么可以提升插入的效率。但是这只是一个提示(需要保证set的顺序)

     返回的iterator 指向插入节点所在的位置(如果元素已经存则插入失败,iterator指向set中已经存在的元素的位置)。


range (3)  template <class InputIterator>    void insert (InputIterator first, InputIterator last);

插入多个元素的结构。返回是void类型。

erase方法

   image

  从set中移除元素。通过看erase的说明。erase时,移除元素对应的迭代器会失效。set中其他元素的指针和迭代器仍然有效。

image

对于set遍历移除时,可以使用 set.erase(it++);的方式。

refer:http://www.cplusplus.com/reference/set/set/

refer:http://www.cplusplus.com/reference/string/string/compare/

引用提供了一些使用set的常见操作和方法。如果要访问set中的最后一个元素,有几种方法可以实现。第一种方法是使用rbegin()函数,它返回一个指向set中最后一个元素的反向迭代器,然后可以使用*运算符来获取该元素的值。第二种方法是使用end()函数返回一个指向set末尾的迭代器,然后通过--运算符将迭代器移动到最后一个元素,再使用*运算符获取该元素的值。第三种方法是直接使用end()函数返回的迭代器,然后使用--运算符将迭代器移动到最后一个元素,再使用*运算符获取该元素的值。提供了一个重载<运算符的示例代码。在这个示例中,定义了一个结构体node,并在结构体中重载了<运算符。通过重载<运算符,我们可以自定义set中元素的比较规则。在main函数中,创建了一个set<node>类型的集合s,并插入了一些元素。通过遍历集合s,我们可以看到集合中的元素按照我们定义的比较规则进行排序。介绍了set中lower_bound和upper_bound函数的使用。lower_bound函数返回大于或等于给定值的第一个元素的迭代器,而upper_bound函数返回大于给定值的第一个元素的迭代器。在示例代码中,首先创建了一个set<int>类型的集合s,并插入了一些元素。然后使用lower_bound和upper_bound函数分别查找给定值的边界,并输出对应的元素值。<span class="em">1</span><span class="em">2</span><span class="em">3</span> #### 引用[.reference_title] - *1* [C++ STLset详解](https://blog.csdn.net/qq_50285142/article/details/122304728)[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^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] - *2* *3* [【c++STL——第八讲】set系列 (常用知识点总结)](https://blog.csdn.net/m0_63233163/article/details/124645182)[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^v92^chatsearchT3_1"}}] [.reference_item style="max-width: 50%"] [ .reference_list ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值