C++ STL set 与 multiset 高级用法详解:自定义排序与仿函数

C++ STL setmultiset 高级用法详解:自定义排序与仿函数

概述

本文将深入探讨 C++ STL 中 setmultiset 容器的高级用法,重点讲解自定义排序规则和**函数对象(仿函数)**的应用。通过分析提供的代码示例,我们将全面理解这些高级特性。

核心知识点

1. 自定义排序规则

setmultiset 默认使用 less<T> 进行比较,产生升序排列。我们可以通过模板参数指定比较方式:

// 默认升序排列(等同于 set<int, less<int>>)
set<int> set1;  

// 使用 greater<int> 实现降序排列
set<int, greater<int>> set1;  
  • less<T>:产生升序排列(默认)
  • greater<T>:产生降序排列
  • 这些是函数对象(也称为仿函数

2. 自定义类在 set 中的使用

set 中存储自定义类时,需要提供比较方式。有两种方法:

方法一:重载 < 运算符
class student {
public:
    // ... 其他代码 ...
    bool operator<(const student& right) const {
        return this->age > right.age;  // 实现降序排列
    }
};
  • 必须声明为 const 成员函数
  • 参数应为 const 引用
  • 返回 bool 值表示比较结果
方法二:使用自定义比较类(仿函数)
class FunStudent {
public:
    bool operator()(const student& left, const student& right) const {
        cout << "调用了 Funstudent" << endl;
        return left.getAge() < right.getAge();
    }
};

// 使用自定义比较类
set<student, FunStudent> setStu;
  • 必须重载 operator()
  • 应该是无状态的(通常声明为 const 成员函数)
  • 可以包含调试输出等额外逻辑

3. 仿函数(函数对象)详解

仿函数是行为类似函数的对象,关键特点:

  1. 通过重载 operator() 实现
  2. 可以拥有状态(成员变量)
  3. 比普通函数更灵活
  4. 常用于 STL 算法的定制行为
// 创建仿函数对象
FunStudent funStu;  

// 像函数一样调用
bool ret = funStu(liXiaoHua, wangDaChui);  

代码解析

1. 基本 set 操作

set<int> setInt;
for (int i = 5; i > 0; i--) {
    set1.insert(i);  // 插入元素
}

// 遍历输出
for (auto it = set1.begin(); it != set1.end(); ++it) {
    cout << *it << " ";
}
  • 无论插入顺序如何,set 会自动排序
  • 使用迭代器遍历时按排序顺序访问

2. 自定义排序的 set

set<int, greater<int>> set1;  // 降序排列
set1.insert(5); set1.insert(1); set1.insert(3);
// 输出顺序将是 5 3 1

3. 存储自定义类

set<student, FunStudent> setStu;
setStu.insert(student(18));
setStu.insert(student(19));

// 输出时将按照 FunStudent 定义的规则排序

关键区别:set vs multiset

特性setmultiset
元素唯一性唯一允许重复
插入效率检查唯一性,稍慢直接插入,较快
内存使用较少可能较多
查找性能O(log n)O(log n)

性能考虑

  1. 插入性能multiset 通常比 set 快,因为不需要检查唯一性
  2. 查找性能:两者相同,都是基于红黑树的 O(log n) 查找
  3. 内存占用multiset 可能占用更多内存,因为允许重复元素

实际应用建议

  1. 需要唯一元素:选择 set
  2. 允许重复元素:选择 multiset
  3. 自定义排序
    • 简单情况:重载 < 运算符
    • 复杂情况:使用自定义比较类
  4. 性能敏感场景
    • 考虑 unordered_set 如果不需要排序
    • 对于大量数据,注意红黑树的平衡性

完整代码总结

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

// 自定义类
class student {
public:
    student(int age) : age(age) {}
    
    // 方法一:重载 < 运算符
    bool operator<(const student& right) const {
        return this->age > right.age; // 降序
    }
    
    int getAge() const { return age; }

private:
    int age;
};

// 方法二:自定义比较类
class FunStudent {
public:
    bool operator()(const student& left, const student& right) const {
        return left.getAge() < right.getAge(); // 升序
    }
};

int main() {
    // 1. 基本 set 使用
    set<int, greater<int>> numSet; // 降序
    for(int i=0; i<5; i++) numSet.insert(i);
    
    // 2. 自定义类 set
    set<student> set1; // 使用重载的 <
    set<student, FunStudent> set2; // 使用仿函数
    
    // 3. 仿函数演示
    FunStudent comp;
    bool result = comp(student(18), student(20));
    
    return 0;
}

常见问题解答

Q1:为什么自定义比较函数要声明为 const?

A:因为 STL 容器可能在常量上下文中使用比较函数,保证线程安全和正确性。

Q2:set 和 map 的排序有什么区别?

A:set 直接对元素排序,map 对键排序。它们的底层实现类似,都是红黑树。

Q3:如何选择重载 < 还是自定义比较类?

A:

  • 如果类有自然的排序规则,重载 <
  • 需要多种排序方式时,使用不同的比较类
  • 比较逻辑复杂时,使用比较类更清晰

通过这篇详细解析,你应该对 setmultiset 的高级用法有了全面理解。记住关键点:自动排序元素唯一性自定义比较仿函数应用,这些是有效使用这两种容器的基础。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值