STL关联容器详解之set

本文详细介绍了C++ STL中的Set容器,包括其特性、初始化方法、插入和删除元素的操作,以及相关成员函数的使用。Set容器存储键值对,元素唯一且自动排序,不支持直接修改元素值。文章还提到了Multiset容器,它是Set容器的变体,允许存储重复元素。
摘要由CSDN通过智能技术生成

参考链接

set 容器

使用 set 容器存储的各个键值对,要求键 key 和值 value 必须相等。基于 set 容器的这种特性,当使用 set 容器存储键值对时,只需要为其提供各键值对中的 value 值(也就是 key 的值)即可。
使用 set 容器存储的各个元素的值必须各不相同。

切勿尝试直接修改 set 容器中已存储元素的值,这很有可能破坏 set 容器中元素的有序性,最正确的修改 set 容器中元素值的做法是:先删除该元素,然后再添加一个修改后的元素。

set 容器定义于<set>头文件,并位于 std 命名空间中。

创建set容器的几种方法

  • 调用默认构造函数,创建空的 set 容器:

    std::set<std::string> myset;
    

    该容器采用默认的std::less<T>规则,会对存储的 string 类型元素做升序排序。

  • 在创建 set 容器的同时,对其进行初始化。

    std::set<std::string> myset{"http://c.biancheng.net/java/",
                                "http://c.biancheng.net/stl/",
                                "http://c.biancheng.net/python/"};
    
  • 在创建新 set 容器的同时,将已有 set 容器中存储的所有元素全部复制到新 set 容器中。

    std::set<std::string> copyset(myset);
    //等同于
    //std::set<std::string> copyset = myset
    
  • 创建新 set 容器的同时,利用临时的 set 容器为其初始化。

    set<string> retSet() {
        std::set<std::string> myset{ "http://c.biancheng.net/java/",
                                "http://c.biancheng.net/stl/",
                                "http://c.biancheng.net/python/" };
        return myset;
    }
    std::set<std::string> copyset(retSet());
    //或者
    //std::set<std::string> copyset = retSet();
    

    由于 retSet() 函数的返回值是一个临时 set 容器,因此在初始化 copyset 容器时,其内部调用的是 set 类模板中的移动构造函数,而非拷贝构造函数。

  • 取已有 set 容器中的部分元素,来初始化新 set 容器。

    std::set<std::string> myset{ "http://c.biancheng.net/java/",
                        "http://c.biancheng.net/stl/",
                        "http://c.biancheng.net/python/" };
    std::set<std::string> copyset(++myset.begin(), myset.end());
    
  • 手动修改 set 容器中的排序规则

    std::set<std::string,std::greater<string> > myset{
        "http://c.biancheng.net/java/",
        "http://c.biancheng.net/stl/",
        "http://c.biancheng.net/python/"};
    

set容器的成员方法

成员方法功能
begin()返回指向容器中第一个(注意,是已排好序的第一个)元素的双向迭代器。如果 set 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。
end()返回指向容器最后一个元素(注意,是已排好序的最后一个)所在位置后一个位置的双向迭代器,通常和 begin() 结合使用。如果 set 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。
rbegin()返回指向最后一个(注意,是已排好序的最后一个)元素的反向双向迭代器。如果 set 容器用 const 限定,则该方法返回的是 const 类型的反向双向迭代器。
rend()返回指向第一个(注意,是已排好序的第一个)元素所在位置前一个位置的反向双向迭代器。如果 set 容器用 const 限定,则该方法返回的是 const 类型的反向双向迭代器。
cbegin()和 begin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的元素值。
cend()和 end() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的元素值。
crbegin()和 rbegin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的元素值。
crend()和 rend() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的元素值。
find(val)在 set 容器中查找值为 val 的元素,如果成功找到,则返回指向该元素的双向迭代器;反之,则返回和 end() 方法一样的迭代器。另外,如果 set 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。
lower_bound(val)返回一个指向当前 set 容器中第一个大于或等于 val 的元素的双向迭代器。如果 set 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。
upper_bound(val)返回一个指向当前 set 容器中第一个大于 val 的元素的迭代器。如果 set 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。
equal_range(val)该方法返回一个 pair 对象(包含 2 个双向迭代器),其中 pair.first 和 lower_bound() 方法的返回值等价,pair.second 和 upper_bound() 方法的返回值等价。也就是说,该方法将返回一个范围,该范围中包含的值为 val 的元素(set 容器中各个元素是唯一的,因此该范围最多包含一个元素)。
empty()若容器为空,则返回 true;否则 false。
size()返回当前 set 容器中存有元素的个数。
max_size()返回 set 容器所能容纳元素的最大个数,不同的操作系统,其返回值亦不相同。
insert()向 set 容器中插入元素。
erase()删除 set 容器中存储的元素。
swap()交换 2 个 set 容器中存储的所有元素。这意味着,操作的 2 个 set 容器的类型必须相同。
clear()清空 set 容器中所有的元素,即令 set 容器的 size() 为 0。
emplace()在当前 set 容器中的指定位置直接构造新元素。其效果和 insert() 一样,但效率更高。
emplace_hint()在本质上和 emplace() 在 set 容器中构造新元素的方式是一样的,不同之处在于,使用者必须为该方法提供一个指示新元素生成位置的迭代器,并作为该方法的第一个参数。
count(val)在当前 set 容器中,查找值为 val 的元素的个数,并返回。注意,由于 set 容器中各元素的值是唯一的,因此该函数的返回值最大为 1
#include <iostream>
#include <set>
#include <string>
using namespace std;
int main()
{
    //创建空set容器
    std::set<std::string> myset;
    //空set容器不存储任何元素
    cout << "1、myset size = " << myset.size() << endl;
    //向myset容器中插入新元素
    myset.insert("http://c.biancheng.net/java/");
    myset.insert("http://c.biancheng.net/stl/");
    myset.insert("http://c.biancheng.net/python/");
    cout << "2、myset size = " << myset.size() << endl;
    //利用双向迭代器,遍历myset
    for (auto iter = myset.begin(); iter != myset.end(); ++iter) {
        cout << *iter << endl;
    }
    return 0;
}

set容器迭代器

set 容器类模板中未提供 at() 成员函数,也未对 [] 运算符进行重载。因此,要想访问 set 容器中存储的元素,只能借助 set 容器的迭代器。
C++ STL 标准库为 set 容器配置的迭代器类型为双向迭代器。这意味着,假设 p 为此类型的迭代器,则其只能进行 ++p、p++、–p、p–、*p 操作,并且 2 个双向迭代器之间做比较,也只能使用 == 或者 != 运算符。
begin()/end()

#include <iostream>
#include <set>
#include <string>
using namespace std;
int main()
{
    //创建并初始化set容器
    std::set<std::string> myset{ "http://c.biancheng.net/java/",
                                 "http://c.biancheng.net/stl/",
                                 "http://c.biancheng.net/python/"
    };
    //利用双向迭代器,遍历myset
    for (auto iter = myset.begin(); iter != myset.end(); ++iter) {
        cout << *iter << endl;
    }
    return 0;
}

因为 iter 迭代器指向的是 set 容器存储的某个元素,而不是键值对,因此通过 *iter 可以直接获取该迭代器指向的元素的值。

如果只想遍历 set 容器中指定区域内的部分数据,则可以借助 find()、lower_bound() 以及 upper_bound() 实现。通过调用它们,可以获取一个指向指定元素的迭代器。

equal_range(val) 函数的返回值是一个 pair 类型数据,其包含 2 个迭代器,表示 set 容器中和指定参数 val 相等的元素所在的区域,但由于 set 容器中存储的元素各不相等,因此该函数返回的这 2 个迭代器所表示的范围中,最多只会包含 1 个元素。

#include <iostream>
#include <set>
#include <string>
using namespace std;
int main()
{
    //创建并初始化set容器
    std::set<std::string> myset{ "http://c.biancheng.net/java/",
                                 "http://c.biancheng.net/stl/",
                                 "http://c.biancheng.net/python/"
    };
   
    set<string>::iterator iter = myset.find("http://c.biancheng.net/python/");
    for (;iter != myset.end();++iter)
    {
        cout << *iter << endl;
    }
    return 0;
}

set 类模板中包含 lower_bound()、upper_bound()、equal_range() 这 3 个成员函数,但它们更适用于 multiset 容器,几乎不会用于操作 set 容器。

set insert()方法

  • 只要给定目标元素的值,insert() 方法即可将该元素添加到 set 容器中

    //普通引用方式传参
    pair<iterator,bool> insert (const value_type& val);
    //右值引用方式传参
    pair<iterator,bool> insert (value_type&& val);
    

    val 表示要添加的新元素,该方法的返回值为 pair 类型。
    以上 2 种语法格式的 insert() 方法,返回的都是 pair 类型的值,其包含 2 个数据,一个迭代器和一个 bool 值:

    • 当向 set 容器添加元素成功时,该迭代器指向 set 容器新添加的元素,bool 类型的值为 true;
    • 如果添加失败,即证明原 set 容器中已存有相同的元素,此时返回的迭代器就指向容器中相同的此元素,同时 bool 类型的值为 false。
    #include <iostream>
    #include <set>
    #include <string>
    using namespace std;
    int main()
    {
        //创建并初始化set容器
        std::set<std::string> myset;
        //准备接受 insert() 的返回值
        pair<set<string>::iterator, bool> retpair;
        //采用普通引用传值方式
        string str = "http://c.biancheng.net/stl/";
        retpair = myset.insert(str);
        cout << "iter->" << *(retpair.first) << " " << "bool = " << retpair.second << endl;
        //采用右值引用传值方式
        retpair = myset.insert("http://c.biancheng.net/python/");
        cout << "iter->" << *(retpair.first) << " " << "bool = " << retpair.second << endl;
        return 0;
    }
    
  • insert() 还可以指定将新元素插入到 set 容器中的具体位置

    //以普通引用的方式传递 val 值
    iterator insert (const_iterator position, const value_type& val);
    //以右值引用的方式传递 val 值
    iterator insert (const_iterator position, value_type&& val);
    

    以上 2 种语法格式中,insert() 函数的返回值为迭代器:

    • 当向 set 容器添加元素成功时,该迭代器指向容器中新添加的元素;
    • 当添加失败时,证明原 set 容器中已有相同的元素,该迭代器就指向 set 容器中相同的这个元素。
    #include <iostream>
    #include <set>
    #include <string>
    using namespace std;
    int main()
    {
        //创建并初始化set容器
        std::set<std::string> myset;
        //准备接受 insert() 的返回值
        set<string>::iterator iter;
        //采用普通引用传值方式
        string str = "http://c.biancheng.net/stl/";
        iter = myset.insert(myset.begin(),str);
        cout << "myset size =" << myset.size() << endl;
        //采用右值引用传值方式
        iter = myset.insert(myset.end(),"http://c.biancheng.net/python/");
        cout << "myset size =" << myset.size() << endl;
        return 0;
    }
    

    使用 insert() 方法将目标元素插入到 set 容器指定位置后,如果该元素破坏了容器内部的有序状态,set 容器还会自行对新元素的位置做进一步调整。也就是说,insert() 方法中指定新元素插入的位置,并不一定就是该元素最终所处的位置。

  • 向当前 set 容器中插入其它 set 容器指定区域内的所有元素,只要这 2 个 set 容器存储的元素类型相同即可。

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

    其中 first 和 last 都是迭代器,它们的组合 [first,last) 可以表示另一 set 容器中的一块区域,该区域包括 first 迭代器指向的元素,但不包含 last 迭代器指向的元素。

    #include <iostream>
    #include <set>
    #include <string>
    using namespace std;
    int main()
    {
        //创建并初始化set容器
        std::set<std::string> myset{ "http://c.biancheng.net/stl/",
                                    "http://c.biancheng.net/python/",
                                    "http://c.biancheng.net/java/" };
        //创建一个同类型的空 set 容器
        std::set<std::string> otherset;
        //利用 myset 初始化 otherset
        otherset.insert(++myset.begin(), myset.end());
        //输出 otherset 容器中的元素
        for (auto iter = otherset.begin(); iter != otherset.end(); ++iter) {
            cout << *iter << endl;
        }
        return 0;
    }
    
  • 采用如下格式的 insert() 方法,可实现一次向 set 容器中添加多个元素:

    void insert ( {E1, E2,...,En} );
    
    #include <iostream>
    #include <set>
    #include <string>
    using namespace std;
    int main()
    {
        //创建并初始化set容器
        std::set<std::string> myset;
        //向 myset 中添加多个元素
        myset.insert({ "http://c.biancheng.net/stl/",
            "http://c.biancheng.net/python/",
            "http://c.biancheng.net/java/" });
        for (auto iter = myset.begin(); iter != myset.end(); ++iter) {
            cout << *iter << endl;
        }
        return 0;
    }
    

emplace()和emplace_hint()

emplace()

template <class... Args>
  pair<iterator,bool> emplace (Args&&... args);

参数 (Args&&… args) 指的是,只需要传入构建新元素所需的数据即可,该方法可以自行利用这些数据构建出要添加的元素。

该方法的返回值类型为 pair 类型,其包含 2 个元素,一个迭代器和一个 bool 值:

  • 当该方法将目标元素成功添加到 set 容器中时,其返回的迭代器指向新插入的元素,同时 bool 值为 true;
  • 当添加失败时,则表明原 set 容器中已存在相同值的元素,此时返回的迭代器指向容器中具有相同键的这个元素,同时 bool 值为 false。
#include <iostream>
#include <set>
#include <string>
using namespace std;
int main()
{
    //创建并初始化 set 容器
    std::set<string>myset;
    //向 myset 容器中添加元素
    pair<set<string, string>::iterator, bool> ret = myset.emplace("http://c.biancheng.net/stl/");
    cout << "myset size = " << myset.size() << endl;
    cout << "ret.iter = <" << *(ret.first) << ", " << ret.second << ">" << endl;
    return 0;
}

emplace_hint()

template <class... Args>
  iterator emplace_hint (const_iterator position, Args&&... args);
  • 该方法需要额外传入一个迭代器,用来指明新元素添加到 set 容器的具体位置(新元素会添加到该迭代器指向元素的前面);
  • 返回值是一个迭代器,而不再是 pair 对象。当成功添加元素时,返回的迭代器指向新添加的元素;反之,如果添加失败,则迭代器就指向 set 容器和要添加元素的值相同的元素。
#include <iostream>
#include <set>
#include <string>
using namespace std;
int main()
{
    //创建并初始化 set 容器
    std::set<string>myset;
    //在 set 容器的指定位置添加键值对
    set<string>::iterator iter = myset.emplace_hint(myset.begin(), "http://c.biancheng.net/stl/");
    cout << "myset size = " << myset.size() << endl;
    cout << *iter << endl;
    return 0;
}

set删除数据:erase()和clear()方法

erase()

//删除 set 容器中值为 val 的元素
size_type erase (const value_type& val);
//删除 position 迭代器指向的元素
iterator  erase (const_iterator position);
//删除 [first,last) 区间内的所有元素
iterator  erase (const_iterator first, const_iterator last);

第 1 种格式的 erase() 方法,其返回值为一个整数,表示成功删除的元素个数;后 2 种格式的 erase() 方法,返回值都是迭代器,其指向的是 set 容器中删除元素之后的第一个元素。

#include <iostream>
#include <set>
#include <string>
using namespace std;
int main()
{
    //创建并初始化 set 容器
    std::set<int>myset{1,2,3,4,5};
    cout << "myset size = " << myset.size() << endl;
   
    //1) 调用第一种格式的 erase() 方法
    int num = myset.erase(2); //删除元素 2,myset={1,3,4,5}
    cout << "1、myset size = " << myset.size() << endl;
    cout << "num = " << num << endl;
    //2) 调用第二种格式的 erase() 方法
    set<int>::iterator iter = myset.erase(myset.begin()); //删除元素 1,myset={3,4,5}
    cout << "2、myset size = " << myset.size() << endl;
    cout << "iter->" << *iter << endl;
    //3) 调用第三种格式的 erase() 方法
    set<int>::iterator iter2 = myset.erase(myset.begin(), --myset.end());//删除元素 3,4,myset={5}
    cout << "3、myset size = " << myset.size() << endl;
    cout << "iter2->" << *iter2 << endl;
    return 0;
}

clear()

删除 set 容器中存储的所有元素

#include <iostream>
#include <set>
#include <string>
using namespace std;
int main()
{
    //创建并初始化 set 容器
    std::set<int>myset{1,2,3,4,5};
    cout << "1、myset size = " << myset.size() << endl;
    //清空 myset 容器
    myset.clear();
    cout << "2、myset size = " << myset.size() << endl;
    return 0;
}

multiset容器

set 容器具有以下几个特性:

  • 不再以键值对的方式存储数据,因为 set 容器专门用于存储键和值相等的键值对,因此该容器中真正存储的是各个键值对的值(value);
  • set 容器在存储数据时,会根据各元素值的大小对存储的元素进行排序(默认做升序排序);
  • 存储到 set 容器中的元素,虽然其类型没有明确用 const 修饰,但正常情况下它们的值是无法被修改的;
  • set 容器存储的元素必须互不相等。

multiset 容器遵循 set 容器的前 3 个特性,仅在第 4 条特性上有差异。和 set 容器不同的是,multiset 容器可以存储多个值相同的元素。
multiset 类模板也定义在<set>头文件,并位于 std 命名空间中。

创建multiset容器的方法

  • 调用默认构造函数,创建空的 multiset 容器

    std::multiset<std::string> mymultiset;
    
  • 在创建 multiset 容器的同时,对其进行初始化。

    std::multiset<std::string> mymultiset{ "http://c.biancheng.net/java/",
                                           "http://c.biancheng.net/stl/",
                                           "http://c.biancheng.net/python/" };
    
  • 在创建新 multiset 容器的同时,将已有 multiset 容器中存储的所有元素全部复制到新 multiset 容器中。

    std::multiset<std::string> copymultiset(mymultiset);
    //等同于
    //std::multiset<std::string> copymultiset = mymultiset;
    

    创建新 multiset 容器的同时,利用临时的 multiset 容器为其初始化。

    multiset<string> retMultiset() {
        std::multiset<std::string> tempmultiset{ "http://c.biancheng.net/java/",
                                "http://c.biancheng.net/stl/",
                                "http://c.biancheng.net/python/" };
        return tempmultiset;
    }
    std::multiset<std::string> copymultiset(retMultiset());
    //等同于
    //std::multiset<std::string> copymultiset = retMultiset();
    

    由于 retMultiset() 函数的返回值是一个临时 multiset 容器,因此在初始化 copymultiset 容器时,其内部调用的是 multiset 类模板中的移动构造函数,而非拷贝构造函数。

  • 取已有 multiset 容器中的部分元素,来初始化新 multiset 容器。

    std::multiset<std::string> mymultiset{ "http://c.biancheng.net/java/",
                                           "http://c.biancheng.net/stl/",
                                           "http://c.biancheng.net/python/" };
    std::set<std::string> copymultiset(++mymultiset.begin(), mymultiset.end());
    
  • 手动修改 multiset 容器中的排序规则。

    std::multiset<std::string, std::greater<string> > mymultiset{
        "http://c.biancheng.net/java/",
        "http://c.biancheng.net/stl/",
        "http://c.biancheng.net/python/" };
    

multiset容器的成员方法

成员方法功能
begin()返回指向容器中第一个(注意,是已排好序的第一个)元素的双向迭代器。如果 multiset 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。
end()返回指向容器最后一个元素(注意,是已排好序的最后一个)所在位置后一个位置的双向迭代器,通常和 begin() 结合使用。如果 multiset 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。
rbegin()返回指向最后一个(注意,是已排好序的最后一个)元素的反向双向迭代器。如果 multiset 容器用 const 限定,则该方法返回的是 const 类型的反向双向迭代器。
rend()返回指向第一个(注意,是已排好序的第一个)元素所在位置前一个位置的反向双向迭代器。如果 multiset 容器用 const 限定,则该方法返回的是 const 类型的反向双向迭代器。
cbegin()和 begin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的元素值。
cend()和 end() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的元素值。
crbegin()和 rbegin() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的元素值。
crend()和 rend() 功能相同,只不过在其基础上,增加了 const 属性,不能用于修改容器内存储的元素值
find(val)在 multiset 容器中查找值为 val 的元素,如果成功找到,则返回指向该元素的双向迭代器;反之,则返回和 end() 方法一样的迭代器。另外,如果 multiset 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。
lower_bound(val)返回一个指向当前 multiset 容器中第一个大于或等于 val 的元素的双向迭代器。如果 multiset 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。
upper_bound(val)返回一个指向当前 multiset 容器中第一个大于 val 的元素的迭代器。如果 multiset 容器用 const 限定,则该方法返回的是 const 类型的双向迭代器。
equal_range(val)该方法返回一个 pair 对象(包含 2 个双向迭代器),其中 pair.first 和 lower_bound() 方法的返回值等价,pair.second 和 upper_bound() 方法的返回值等价。也就是说,该方法将返回一个范围,该范围中包含所有值为 val 的元素。
empty()若容器为空,则返回 true;否则 false。
size()返回当前 multiset 容器中存有元素的个数。
max_size()返回 multiset 容器所能容纳元素的最大个数,不同的操作系统,其返回值亦不相同。
insert()向 multiset 容器中插入元素。
erase()删除 multiset 容器中存储的指定元素。
swap()交换 2 个 multiset 容器中存储的所有元素。这意味着,操作的 2 个 multiset 容器的类型必须相同。
clear()清空 multiset 容器中所有的元素,即令 multiset 容器的 size() 为 0。
emplace()在当前 multiset 容器中的指定位置直接构造新元素。其效果和 insert() 一样,但效率更高。
emplace_hint()本质上和 emplace() 在 multiset 容器中构造新元素的方式是一样的,不同之处在于,使用者必须为该方法提供一个指示新元素生成位置的迭代器,并作为该方法的第一个参数。
count(val)在当前 multiset 容器中,查找值为 val 的元素的个数,并返回。

由于 multiset 容器允许存储多个值相同的元素,因此诸如 count()、find()、lower_bound()、upper_bound()、equal_range()等方法,更常用于 multiset 容器。

#include <iostream>
#include <set>
#include <string>
using namespace std;
int main() {
    std::multiset<int> mymultiset{1,2,2,2,3,4,5};
    cout << "multiset size = " << mymultiset.size() << endl;
    cout << "multiset count(2) =" << mymultiset.count(2) << endl;
    //向容器中添加元素 8
    mymultiset.insert(8);
    //删除容器中所有值为 2 的元素
    int num = mymultiset.erase(2);
    cout << "删除了 " << num << " 个元素 2" << endl;
    //输出容器中存储的所有元素
    for (auto iter = mymultiset.begin(); iter != mymultiset.end(); ++iter) {
        cout << *iter << " ";
    }
    return 0;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值