信息学奥赛刷题实战:OpenJudge NOI 1.11 08题,手把手教你用C++ STL的set容器5行代码搞定去重排序

信息学奥赛刷题实战:用C++ STL的set容器5行代码搞定去重排序

在信息学竞赛的战场上,时间就是生命。当你面对一道需要去重排序的题目时,是选择手写几十行的排序算法,还是用标准库提供的现成工具?本文将带你领略C++ STL中set容器的魔力,用5行核心代码解决OpenJudge NOI 1.11 08题,同时深入探讨这种方法的优势与适用场景。

1. 为什么选择STL容器而非手写算法?

在解决"不重复地输出数"这类经典问题时,传统解法往往需要以下步骤:

  1. 实现一个排序算法(如快速排序、归并排序)
  2. 遍历排序后的数组,跳过重复元素
  3. 处理边界条件和特殊情况

这种方法的典型代码量在30-50行左右,而且存在几个明显问题:

  • 容易出错 :手写排序算法边界条件复杂
  • 维护成本高 :每次使用都需要重新实现或复制粘贴
  • 可读性差 :算法实现细节会分散对核心逻辑的注意力

相比之下,使用STL的set容器具有以下优势:

对比维度 手写算法 STL set容器
代码量 30-50行 5-10行
可读性 中等
维护性
执行效率 O(nlogn) O(nlogn)
额外空间 O(1)或O(n) O(n)

提示:在竞赛编程中,正确性和编码速度往往比微小的性能优化更重要。set容器虽然空间开销略大,但在时间压力下是更优选择。

2. set容器的核心特性与原理

set是C++标准库中的关联容器,基于红黑树实现,具有以下关键特性:

  • 自动排序 :元素插入后自动按升序排列
  • 唯一性保证 :容器内不会存在重复元素
  • 高效查找 :查找操作时间复杂度为O(logn)
#include <set>
using namespace std;

set<int> unique_numbers;  // 声明一个存储整数的set

set的内部工作机制可以简单理解为:

  1. 当插入新元素时,容器会自动找到合适的插入位置
  2. 如果元素已存在,则插入操作不会改变容器内容
  3. 所有元素始终保持有序状态

这种特性正好完美契合"去重+排序"的需求,让我们看看如何用在实际解题中。

3. 五行核心代码解决方案

针对OpenJudge NOI 1.11 08题,使用set容器的完整解决方案如下:

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

int main() {
    set<int> s;  // 1. 创建set容器
    int n, x; cin >> n;
    while(n--) { cin >> x; s.insert(x); }  // 2. 插入所有元素
    for(auto num : s) cout << num << " ";  // 3. 输出已排序且去重的元素
    return 0;
}

代码解析:

  1. set<int> s :创建一个空的set容器,用于存储整数
  2. s.insert(x) :将输入的数字插入set,自动处理排序和去重
  3. 范围for循环:按升序遍历并输出所有元素

与原文中的各种解法相比,这种方法的优势显而易见:

  • 代码量减少80%以上
  • 无需关心排序算法实现细节
  • 逻辑清晰,意图明确
  • 几乎不可能出现边界条件错误

4. 性能分析与优化建议

虽然set容器的解决方案简洁优雅,但了解其性能特点对竞赛编程至关重要:

4.1 时间复杂度分析

  • 插入n个元素:每个插入操作O(logn),总时间O(nlogn)
  • 遍历输出:O(n)
  • 总体复杂度:O(nlogn),与最优排序算法相同

4.2 空间复杂度

  • set容器需要存储所有唯一元素:O(n)
  • 相比某些原地排序算法(如快速排序)的O(1)空间,这是主要的trade-off

4.3 实际性能考量

在信息学竞赛常见的输入规模(n≤10^5)下,set解法完全能够胜任。以下是一些优化建议:

  1. 提前预留空间 :对于已知大致规模的情况,可以使用 reserve 方法

    s.reserve(100000);  // 预分配内存,减少动态扩容开销
    
  2. 使用更快的输入方法 :当处理大量数据时,可以替换cin为更快的输入方式

    ios::sync_with_stdio(false);
    cin.tie(nullptr);
    
  3. 考虑unordered_set+手动排序 :如果去重和排序可以分步进行,这种组合有时更快

5. 扩展应用:其他STL容器的选择

虽然set是最直接的解决方案,但根据具体问题特点,其他STL容器也可能适用:

5.1 vector+sort+unique组合

vector<int> v;
// ... 输入所有元素 ...
sort(v.begin(), v.end());
auto last = unique(v.begin(), v.end());
v.erase(last, v.end());

适用场景:

  • 需要保留原始数据副本
  • 去重后还需要进行其他操作

5.2 unordered_set+vector排序

unordered_set<int> us;
// ... 插入所有元素去重 ...
vector<int> v(us.begin(), us.end());
sort(v.begin(), v.end());

适用场景:

  • 输入规模极大,且重复率很高
  • 需要分阶段处理去重和排序

5.3 multiset的使用

如果需要保留元素出现次数信息,可以考虑multiset:

multiset<int> ms;
// ... 插入元素 ...
// 输出时可以通过相邻元素比较来去重

容器选择决策树:

  1. 是否需要自动去重+排序? → 选择set
  2. 是否需要知道元素出现次数? → 选择multiset
  3. 是否数据量极大且重复率高? → 考虑unordered_set+手动排序
  4. 是否需要保留原始数据? → 选择vector+sort+unique

在实际竞赛中,set通常是最省时省力的选择,特别是在时间紧迫的情况下。理解这些工具的特性,能够帮助你在比赛中快速做出最佳选择。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值