STL教程(七): 关联容器--multiset/set

本文介绍了C++ STL中multiset和set容器的基本概念,包括它们的区别(元素唯一性与重复性)、排序机制、构造函数、成员函数如插入、删除及查找,以及如何通过重载、仿函数和全局函数自定义排序。实例演示了如何通过Person类实例展示自定义排序操作。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、multiset/set简介

// T:multiset的键值类型
// Compare:一个二元谓词,即接受两个T类型参数并返回一个bool,默认为less<T> 即递增
// Alloc:用于定义存储分配模型的分配器对象的类型。默认使用分配器类模板,它定义了最简单的内存分配模型
template < class T,                        // multiset::key_type/value_type
           class Compare = less<T>,        // multiset::key_compare/value_compare
           class Alloc = allocator<T> >    // multiset::allocator_type
           > class multiset;

set的声明与multiset完全相同,这里就不再累述了

multiset/set是一个关联容器,其底层实现通常是红黑树(二叉搜索树),multiset/set容器内的元素的值和键是同一个值,在set中,每个键值都是唯一的,不可能重复,但是在multiset中的键值却是可以重复的。它们的排序是通过比较函数(一元谓词)Compare 进行排序的,默认为less<T> (升序)。而且multiset/set中的元素值都是不可更改的(元素始终为const类型),但是我们可以在容器中对其进行插入或删除操作。

multiset/set的搜索、删除和插入操作非常快,时间复杂度为logn。

二、multiset/set的成员函数

multiset的成员函数与set完全相同,下面以set为例进行介绍:

1、构造函数

(1)构造一个空的set容器,没有元素,也是默认构造函数

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

explicit set (const allocator_type& alloc);

(2)构造一个包含了范围[first,last)元素的set容器,每个元素都从该范围内的相应元素以相同的顺序就地构造。

template <class InputIterator>
  set (InputIterator first, InputIterator last,
       const key_compare& comp = key_compare(),
       const allocator_type& = allocator_type());


(3) 以set容器x作为数据源,构造一个新set容器,其中新set容器中的元素来自于x中元素拷贝的副本。

set (const set& x);
set (const set& x, const allocator_type& alloc);

(4)构造一个容器,并直接从x中移动元素,之后x中的元素将被移除

set (set&& x);
set (set&& x, const allocator_type& alloc);

(5)以相同的顺序构造一个set容器,其中包含l中每个元素拷贝的副本

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

2、operator=函数

(1)使用set列表other对其进行复制拷贝

set& operator=( const set& other );

(2)使用set列表other对其进行移动拷贝,之后other的数据将被清空

set& operator=( set&& other );

(3)使用初始化列表对set进行赋值

set& operator=( std::initializer_list<T> l );

3、迭代器

iterator begin();  // 返回容器的迭代器到第一个元素
const_iterator cbegin(); // 返回容器的迭代器到第一个元素
iterator end();    // 返回容器的迭代器到最后一个元素 
const_iterator cend();   // 返回容器的迭代器到最后一个元素
reverse_iterator rbegin(); // 返回容器的反向迭代器到第一个元素
const_reverse_iterator crbegin();// 返回容器的反向迭代器到第一个元素
reverse_iterator rend();   // 返回容器的反向迭代器到最后一个元素
const_reverse_iterator crend();  // 返回容器的反向迭代器到最后一个元素

4、容量相关

bool empty(); // 判断容器是否为空
size_type size();// 返回容器中当前元素个数
size_type max_size(); //返回容器内可存放的最大元素个数

5、修改容器

/* 向容器内插入一个元素,返回一个pair,其成员pair::first设置为一个迭代器,
指向新插入的元素或已存在在set 中的元素。如果插入成功,则pair::second元素为true,若该元素已存在
则为false */
pair<iterator,bool> insert (const value_type& val);
pair<iterator,bool> insert (value_type&& val);

/* 提示在position位置插入新元素val(仅仅是提示,并不一定是在position位置),
 以提高元素插入速度,并返回新元素位置或在set中值为val的元素位置 */
iterator insert (const_iterator position, const value_type& val);
iterator insert (const_iterator position, value_type&& val);

// 插入新元素[first,last)
template <class InputIterator>
  void insert (InputIterator first, InputIterator last);

// 将初始化列表l内的元素插入到容器中
void insert (initializer_list<value_type> l);

// 删除位于position的元素,并返回position之后的迭代器
iterator erase (const_iterator position);

// 删除值为val的元素,并返回删除元素的总个数
size_type erase (const value_type& val);

// 删除位置为[first,last)的元素,并返回最后一个移除元素之后的迭代器
iterator  erase (const_iterator first, const_iterator last);

// 用x的内容交换容器的内容
void swap (set& x);

// 清空容器内所有元素
void clear() ;

/* 以args为参数就地构造新元素,并将其插入到容器中。则其成员pair::first设置为一个迭代器,
指向新插入的元素或已存在在set 中的元素。如果插入成功,则pair::second元素为true,若该元
素已存在,则为false */
pair<iterator,bool> emplace (Args&&... args);

/* 其作用同emplace,但该函数使用position参数作为提示,指示该元素的可能位置,
将极大的加快了插入过程 */
iterator emplace_hint (const_iterator position, Args&&... args);

6、查找相关

// 返回容器中元素的键值为key的元素个数 
size_type count( const Key& key ) const

// 查找容器元素中键值为key的元素,并返回其迭代器位置
iterator find( const Key& key );
const_iterator find( const Key& key ) const;

// 查找容器中符合特定要求的键值,并返回两个迭代器:一个指向不小于key的第一个元素,一个指向大于key的第一个元素
std:: pair < iterator,iterator > equal_range ( const Key & key ) ;
std:: pair < const_iterator,const_iterator > equal_range ( const Key & key ) const ;

// 查找容器中键值不小于(即大于或等于)key 的第一个元素的迭代器
iterator lower_bound( const Key& key );
const_iterator lower_bound( const Key& key ) const;

// 查找容器中键值大于key 的第一个元素的迭代器
iterator upper_bound( const Key& key );
const_iterator upper_bound( const Key& key ) const;

7、比较函数 

// 返回比较key的函数对象,它是容器构造时的参数comp的副本,并且因为set的key与value完全相同,所以它与value_comp也相同
key_compare key_comp() const;

// 返回比较value的函数对象,它是容器构造时的参数comp的副本,并且因为set的key与value完全相同,所以它与value_comp也相同
value_compare value_comp()const;

三、自定义排序 

其实在 STL 标准库中,本就包含几个可供关联式容器使用的排序规则,如表 1 表示:

表 1 C++ STL标准库适用于关联式容器的排序规则
排序规则功能
std::less<T>   底层采用 < 运算符实现升序排序,各关联式容器默认采用的排序规则。
std::greater<T>底层采用 > 运算符实现降序排序,同样适用于各个关联式容器。
std::less_equal<T>底层采用 <= 运算符实现升序排序,多用于 multimap 和 multiset 容器。
std::greater_equal<T>底层采用 >= 运算符实现降序排序,多用于 multimap 和 multiset 容器。

1、重载容器元素类型的<符号

#include<iostream>
#include<string>
using namespace std;
#include<set>

struct Person {
    string name;
    int age;
    Person(string _name, int _age) : name(_name), age(_age){}
    bool operator <(const Person &rhs) const {
        if (name != rhs.name) return name < rhs.name;
        else return age < rhs.age;
    }
    friend  ostream  &operator<<(ostream &os, const Person &per){  //声明为友元,重载输出运算符
        os << per.name << "   " << per.age << std::endl;
        return os;
    }
};

int main(){
    set<Person> personSet;
    personSet.insert(Person("za", 2));
    personSet.insert(Person("zb", 5));
    personSet.insert(Person("za", 3));
    personSet.insert(Person("zb", 4));
    for (auto it = personSet.begin(); it != personSet.end(); it++){
        cout << *it;
    }
    return 0;
}
// 输出结果为(za,2) (za,3) (zb,4) (zb,5)

2、仿函数(struct或class)

#include<iostream>
#include<string>
using namespace std;
#include<set>

struct Person {
    string name;
    int age;
    Person(string _name, int _age) : name(_name), age(_age){}
    friend  ostream  &operator<<(ostream &os, const Person &per){  //声明为友元
        os << per.name << "   " << per.age << std::endl;
        return os;
    }
};
typedef struct{
    bool operator ()(const Person lhs, const Person rhs){
        if (lhs.name != rhs.name) return lhs.name < rhs.name;
        return lhs.age < rhs.age;
    }
}Compare;

// 或者使用class类
//class Compare{
//    bool operator ()(const Person lhs, const Person rhs){
//        if (lhs.name != rhs.name) return lhs.name < rhs.name;
//        return lhs.age < rhs.age;
//    }
//};

int main(){
    
    set<Person,Compare> personSet;
    personSet.insert(Person("za", 2));
    personSet.insert(Person("zb", 5));
    personSet.insert(Person("za", 3));
    personSet.insert(Person("zb", 4));
    for (auto it = personSet.begin(); it != personSet.end(); it++){
        cout << *it;
    }
    return 0;
}
// 输出结果为(za,2) (za,3) (zb,4) (zb,5)

3、使用全局函数

#include<iostream>
#include<string>
using namespace std;
#include<set>

struct Person {
    string name;
    int age;
    Person(string _name, int _age) : name(_name), age(_age){}
    friend  ostream  &operator<<(ostream &os, const Person &per){  //声明为友元
        os << per.name << "   " << per.age << std::endl;
        return os;
    }
};

bool cmp(const Person lhs, const Person rhs){
    if (lhs.name != rhs.name) return lhs.name < rhs.name;
    return lhs.age < rhs.age;
}

int main(){
    
    set<Person,decltype(cmp)*> personSet(cmp);
    personSet.insert(Person("za", 2));
    personSet.insert(Person("zb", 5));
    personSet.insert(Person("za", 3));
    personSet.insert(Person("zb", 4));
    personSet.emplace("az", 23);
    for (auto it = personSet.begin(); it != personSet.end(); it++){
        cout << *it;
    }
    return 0;
}

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

Chiang木

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

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

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

打赏作者

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

抵扣说明:

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

余额充值