Lesson 0002 · sort 与 string:排序和字符串

① 程序设计基础 — 第 1 课(续)

上节课:用 vector 把数据装进了容器,读完 n 个数就能随便用。
这节课:数据装好之后要处理它。竞赛题里出场率最高的两件事就是排序(sort)和字符串(string),而且它们各有一个必背的坑。学完这一课,你手里就有一套「读入 → 排序 → 去重 → 输出」的完整套路。
三句话速览

1. sort:一行代码排好序

sort 在 <algorithm> 头文件里,底层是快排 + 堆排 + 插排的混合,复杂度 O(n log n)。你只需要记住三个用法:

#include <iostream>
#include <vector>
#include <algorithm>      // sort 在这里
using namespace std;

int main() {
    vector<int> v = {3, 1, 4, 1, 5, 9, 2};

    sort(v.begin(), v.end());         // 升序:1 1 2 3 4 5 9
    sort(v.rbegin(), v.rend());       // 降序:9 5 4 3 2 1 1

    // 只排前 4 个
    sort(v.begin(), v.begin() + 4);   // v[0..3] 排序,其余不动
}
记忆口诀:sort(开始位置, 结束位置)。结束位置是 最后一个元素的下一个位置(左闭右开)。排整个 vector 就是 v.begin() 到 v.end()。

真题模式:排序 + 去重

这是竞赛中的经典组合。比如洛谷 P1059「明明的随机数」—— 输入 N 个数,排序去重后输出。

#include <iostream>
#include <vector>
#include <algorithm>
using namespace std;

int main() {
    int n;
    cin >> n;
    vector<int> a(n);
    for (int i = 0; i < n; i++) cin >> a[i];

    sort(a.begin(), a.end());                           // 1. 排好序
    a.erase(unique(a.begin(), a.end()), a.end());       // 2. 去重

    cout << a.size() << endl;
    for (int x : a) cout << x << ' ';
    return 0;
}
unique 只是把重复元素「挤」到末尾,返回新末尾的迭代器。
真正把后面那些垃圾删掉的是 erase。两行必须一起用,少一行就等于没去重。

2. string:加强版字符 vector

string 本质上就是一个 vector<char>,再加上字符串专属操作。你会用 vector,就会用 string。

操作代码说明
读入cin >> s;读一个单词(遇空格/换行停)
读整行getline(cin, s);读到换行(含空格)
长度s.size()字符个数
取字符s[i]第 i 个字符,从 0 开始
追加s += "hello";拼接到末尾
比较s == t s < t字典序比较
子串s.substr(i, len)从 i 开始取 len 个字符
查找s.find("abc")返回下标,找不到返回 string::npos

示例:反转字符串

string s;
cin >> s;

// 方法 1:手写循环
for (int i = s.size() - 1; i >= 0; i--) {
    cout << s[i];
}

// 方法 2:STL 一键
reverse(s.begin(), s.end());
cout << s;
reverse 也在 <algorithm> 里,任何能用 begin() / end() 的容器都能反转。

3. 坑点清单

4. 小测

Q1:执行 sort(v.begin(), v.begin() + 3) 会排序几个元素?
Q2:"apple" < "banana" 的结果是?
Q3:cin >> s 读入 "hello world" 后,s 的值是?

5. 动手练习

主题题目做法
排序洛谷 P1059 · 明明的随机数直接套用上面的排序 + 去重模版
字符串洛谷 P1125 · 笨小猴统计字母出现次数,找最大值和最小值(提示:用 vector<int>(26) 当计数器)

卡住时的自查顺序

  1. 样例过了吗?先把排序后的数组打印出来,确认 sort 的范围没写错。
  2. 去重那两行是不是一起写的?只写 unique 的话,输出的个数还是原来那么多。
  3. 字符串题:读的是单词还是整行?带空格就必须换成 getline(cin, s)。
  4. 输出格式对齐了吗?个数和数列分两行、数字之间一个空格,都是判题机会较真的地方。

本课小结

下一课预告
0003 变量、分支与函数 —— 把类型补全(long long、double、char、bool),把 if / else if / else 写顺,再学会把一段逻辑打包成函数。有了函数,后面那些「排序」「判断」就能反复调用,而不是每次重抄一遍。
这一课的三个坑(右开区间、unique 要配 erase、cin >> 遇空格停)会在后面反复出现,建议现在就把它们背下来。哪一句还不明白 —— 比如「unique 到底把重复元素挪到哪了」「string::npos 是什么值」「降序为什么用 rbegin」—— 直接问。

Lesson 0002 · 预计用时 30 min

上一课:vector · Mission · STL 速查表

在线 洛谷 P1059 · 洛谷 P1125