20260227 152127 Cpp 入门第十课

20260227_152127_CPP_入门第十课.md

第十章:C++的百宝箱——STL(标准模板库)

你好!欢迎来到第十章!在前面的章节,我们学习了数组、链表、栈、队列等数据结构,但每次都要自己写代码实现,很麻烦。其实C++已经给我们准备了一个强大的百宝箱,里面有很多常用的数据结构和算法,可以直接拿来用,这就是STL(Standard Template Library,标准模板库)。学完本章,你就能轻松地使用各种容器和算法,让编程变得事半功倍!


10.1 STL程序范例

先来看一个简单的STL程序:使用 vector(动态数组)存储一些整数,然后用 sort 排序,最后用迭代器输出。

#include <iostream>
#include <vector>   // vector容器
#include <algorithm> // 算法,比如sort
using namespace std;

int main() {
    // 创建一个vector,存储整数
    vector<int> numbers;

    // 向尾部添加元素
    numbers.push_back(5);
    numbers.push_back(2);
    numbers.push_back(8);
    numbers.push_back(1);
    numbers.push_back(9);

    cout << "排序前:";
    for (int i = 0; i < numbers.size(); i++) {
        cout << numbers[i] << " ";
    }
    cout << endl;

    // 使用STL的排序算法
    sort(numbers.begin(), numbers.end());

    cout << "排序后:";
    // 使用迭代器遍历
    for (vector<int>::iterator it = numbers.begin(); it != numbers.end(); ++it) {
        cout << *it << " ";
    }
    cout << endl;

    return 0;
}

运行结果

排序前:5 2 8 1 9 
排序后:1 2 5 8 9 

这个程序展示了STL的核心三部分:容器(vector)、算法(sort)、迭代器(iterator)。下面我们一一学习。


10.2 STL概述

STL是C++标准库的一部分,它提供了三大组件:

  • 容器:存储数据的结构,比如动态数组、链表、栈、队列、集合、映射等。
  • 算法:对容器中的数据进行操作的函数,比如排序、查找、替换等。
  • 迭代器:连接容器和算法的桥梁,像指针一样访问容器中的元素。

使用STL的好处:代码简洁、高效、可靠,不用自己重复造轮子。


10.3 容器

容器是用来存放数据的对象。根据组织方式不同,分为序列式容器关联式容器

10.3.1 vector(动态数组)

vector 是一个可以动态增长的数组,支持随机访问(用下标),在尾部插入删除效率高。

基本操作
#include <iostream>
#include <vector>
using namespace std;

int main() {
    // 定义
    vector<int> v1;               // 空vector
    vector<int> v2(5, 10);        // 5个元素,每个都是10
    vector<int> v3 = {1, 2, 3, 4}; // 初始化列表(C++11)

    // 添加元素
    v1.push_back(20);   // 尾部添加
    v1.push_back(30);

    // 访问元素
    cout << v1[0] << endl;         // 下标访问,不检查越界
    cout << v1.at(1) << endl;      // at会检查越界,抛出异常

    // 获取大小
    cout << "v1大小:" << v1.size() << endl;

    // 遍历
    for (int i = 0; i < v1.size(); i++) {
        cout << v1[i] << " ";
    }
    cout << endl;

    // 使用迭代器遍历
    for (vector<int>::iterator it = v1.begin(); it != v1.end(); ++it) {
        cout << *it << " ";
    }
    cout << endl;

    // 使用范围for循环(C++11)
    for (int x : v1) {
        cout << x << " ";
    }
    cout << endl;

    // 删除最后一个元素
    v1.pop_back();

    // 清空
    v1.clear();

    return 0;
}

常用成员函数
- push_back():尾部添加
- pop_back():删除尾部
- size():元素个数
- empty():是否为空
- clear():清空
- begin()end():返回首尾迭代器
- insert()erase():插入和删除(较复杂,需要迭代器)

10.3.2 list(双向链表)

list 是双向链表,不支持随机访问,但插入和删除非常快(尤其在中间)。

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

int main() {
    list<int> lst = {5, 2, 8, 1, 9};

    // 尾部添加
    lst.push_back(10);
    // 头部添加
    lst.push_front(0);

    // 遍历(只能用迭代器,不能用下标)
    for (list<int>::iterator it = lst.begin(); it != lst.end(); ++it) {
        cout << *it << " ";
    }
    cout << endl;

    // 排序(list有自己的sort成员函数,比通用算法快)
    lst.sort();

    for (int x : lst) {
        cout << x << " ";
    }
    cout << endl;

    return 0;
}

10.3.3 deque(双端队列)

deque 是双端队列,支持在头尾快速插入删除,也支持随机访问(但比vector稍慢)。

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

int main() {
    deque<int> dq = {1, 2, 3};
    dq.push_back(4);    // 尾部加
    dq.push_front(0);   // 头部加

    for (int i = 0; i < dq.size(); i++) {
        cout << dq[i] << " ";   // 支持下标
    }
    cout << endl;

    dq.pop_back();      // 删除尾部
    dq.pop_front();     // 删除头部
    return 0;
}

10.3.4 stack(栈)

stack 是适配器容器,它基于其他容器(默认deque)实现,提供后进先出(LIFO)接口。

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

int main() {
    stack<int> st;
    st.push(10);   // 入栈
    st.push(20);
    st.push(30);

    cout << "栈顶元素:" << st.top() << endl; // 30
    st.pop();      // 出栈(无返回值)
    cout << "栈顶元素:" << st.top() << endl; // 20
    cout << "栈大小:" << st.size() << endl;  // 2

    while (!st.empty()) {
        cout << st.top() << " ";
        st.pop();
    }
    return 0;
}

10.3.5 queue(队列)

queue 也是适配器容器,提供先进先出(FIFO)接口。

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

int main() {
    queue<int> q;
    q.push(10);   // 入队
    q.push(20);
    q.push(30);

    cout << "队头:" << q.front() << endl; // 10
    cout << "队尾:" << q.back() << endl;  // 30
    q.pop();      // 出队(队头)
    cout << "队头:" << q.front() << endl; // 20
    return 0;
}

10.3.6 set(集合)

set 是一个有序的集合,元素唯一,自动排序(默认升序)。查找效率高(红黑树)。

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

int main() {
    set<int> s;
    s.insert(5);
    s.insert(2);
    s.insert(8);
    s.insert(2);   // 重复插入无效

    // 遍历(有序)
    for (set<int>::iterator it = s.begin(); it != s.end(); ++it) {
        cout << *it << " ";
    }
    cout << endl;

    // 查找
    set<int>::iterator it = s.find(5);
    if (it != s.end()) {
        cout << "找到了:" << *it << endl;
    } else {
        cout << "没找到" << endl;
    }

    // 删除
    s.erase(2);
    return 0;
}

10.3.7 map(映射)

map 存储键值对(key-value),键唯一,自动排序。

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

int main() {
    map<string, int> scores;
    scores["小明"] = 98;        // 通过键赋值
    scores["小红"] = 95;
    scores["小刚"] = 88;

    // 遍历
    for (map<string, int>::iterator it = scores.begin(); it != scores.end(); ++it) {
        cout << it->first << " : " << it->second << endl;   // first是键,second是值
    }

    // 查找
    string name = "小红";
    map<string, int>::iterator it = scores.find(name);
    if (it != scores.end()) {
        cout << name << "的成绩是:" << it->second << endl;
    } else {
        cout << "没找到" << endl;
    }

    return 0;
}

常用成员函数
- insert(make_pair(key, value)) 或直接用 [key] = value(但 [] 如果键不存在会创建)
- find(key):查找,返回迭代器,找不到返回 end()
- erase(key):删除


10.4 迭代器

迭代器是一种类似于指针的对象,用来遍历容器中的元素。所有容器都提供 begin()end() 成员函数,返回指向第一个元素和最后一个元素之后位置的迭代器。

迭代器分类(按功能):
- 输入迭代器:只读,一次遍历
- 输出迭代器:只写,一次遍历
- 前向迭代器:可读写,只能向前(如 forward_list
- 双向迭代器:可读写,能前后移动(如 listsetmap
- 随机访问迭代器:可读写,支持 +-[] 等(如 vectordeque

使用示例

vector<int> v = {1, 2, 3, 4, 5};

// 正向迭代器
for (vector<int>::iterator it = v.begin(); it != v.end(); ++it) {
    *it = *it * 2;   // 修改元素
}

// 常量迭代器(只读)
for (vector<int>::const_iterator it = v.cbegin(); it != v.cend(); ++it) {
    cout << *it << " ";
}

// 反向迭代器
for (vector<int>::reverse_iterator rit = v.rbegin(); rit != v.rend(); ++rit) {
    cout << *rit << " ";
}

C++11 可以用 auto 简化:

for (auto it = v.begin(); it != v.end(); ++it) { ... }

10.5 算法

STL提供了大量通用算法,都在 <algorithm> 头文件中。它们通过迭代器操作容器,不依赖于具体容器类型。

10.5.1 sort 排序

#include <algorithm>
vector<int> v = {5, 2, 8, 1, 9};
sort(v.begin(), v.end());                // 升序
sort(v.begin(), v.end(), greater<int>()); // 降序

也可以自定义排序规则(用函数或函数对象):

bool cmp(int a, int b) {
    return a > b;   // 降序
}
sort(v.begin(), v.end(), cmp);

10.5.2 find 查找

vector<int> v = {5, 2, 8, 1, 9};
auto it = find(v.begin(), v.end(), 8);
if (it != v.end()) {
    cout << "找到了,位置:" << it - v.begin() << endl;
} else {
    cout << "没找到" << endl;
}

10.5.3 其他常用算法

  • reverse:反转
    cpp reverse(v.begin(), v.end());
  • count:计数
    cpp int cnt = count(v.begin(), v.end(), 5);
  • max_elementmin_element:找最大最小值
    cpp auto maxIt = max_element(v.begin(), v.end()); cout << "最大值:" << *maxIt << endl;
  • binary_search:二分查找(要求容器有序)
    cpp if (binary_search(v.begin(), v.end(), 8)) { ... }
  • copy:复制
    cpp vector<int> v2(v.size()); copy(v.begin(), v.end(), v2.begin());

10.5.4 算法与迭代器的配合

算法通过迭代器操作数据,所以同一个算法可以用于不同的容器。例如 find 可以用于 vector、list、deque 等。


10.6 编程实例讲解

实例1:统计学生成绩(使用vector和map)

题目:输入若干学生姓名和成绩,以“end”结束。然后输出所有学生的成绩,并按成绩从高到低排序输出。

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

struct Student {
    string name;
    int score;
};

bool cmp(const Student &a, const Student &b) {
    return a.score > b.score;   // 降序
}

int main() {
    vector<Student> students;
    string name;
    int score;

    cout << "请输入学生姓名和成绩(输入end结束):" << endl;
    while (true) {
        cin >> name;
        if (name == "end") break;
        cin >> score;
        students.push_back({name, score});
    }

    // 排序
    sort(students.begin(), students.end(), cmp);

    // 输出
    cout << "\n成绩单(按分数降序):" << endl;
    for (const auto &s : students) {
        cout << s.name << " : " << s.score << endl;
    }

    return 0;
}

实例2:单词计数器(使用map)

题目:输入一行英文句子,统计每个单词出现的次数(忽略大小写)。

#include <iostream>
#include <map>
#include <string>
#include <cctype>
#include <sstream>   // 字符串流
using namespace std;

string toLower(string s) {
    for (char &c : s) {
        c = tolower(c);
    }
    return s;
}

int main() {
    string line;
    cout << "请输入一行英文:";
    getline(cin, line);

    map<string, int> wordCount;
    stringstream ss(line);
    string word;

    while (ss >> word) {
        word = toLower(word);
        // 去掉标点符号(简单处理:只保留字母数字)
        string clean;
        for (char c : word) {
            if (isalnum(c)) {
                clean += c;
            }
        }
        if (!clean.empty()) {
            wordCount[clean]++;
        }
    }

    cout << "单词统计:" << endl;
    for (const auto &pair : wordCount) {
        cout << pair.first << " : " << pair.second << endl;
    }

    return 0;
}

实例3:集合运算(使用set)

题目:输入两个集合(整数),输出它们的并集、交集和差集。

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

int main() {
    set<int> set1, set2;
    int n, x;

    cout << "请输入第一个集合的元素个数:";
    cin >> n;
    cout << "请输入" << n << "个整数:";
    for (int i = 0; i < n; i++) {
        cin >> x;
        set1.insert(x);
    }

    cout << "请输入第二个集合的元素个数:";
    cin >> n;
    cout << "请输入" << n << "个整数:";
    for (int i = 0; i < n; i++) {
        cin >> x;
        set2.insert(x);
    }

    // 并集
    set<int> unionSet;
    set_union(set1.begin(), set1.end(), set2.begin(), set2.end(),
              inserter(unionSet, unionSet.begin()));
    cout << "并集:";
    for (int v : unionSet) cout << v << " ";
    cout << endl;

    // 交集
    set<int> interSet;
    set_intersection(set1.begin(), set1.end(), set2.begin(), set2.end(),
                     inserter(interSet, interSet.begin()));
    cout << "交集:";
    for (int v : interSet) cout << v << " ";
    cout << endl;

    // 差集(set1 - set2)
    set<int> diffSet;
    set_difference(set1.begin(), set1.end(), set2.begin(), set2.end(),
                   inserter(diffSet, diffSet.begin()));
    cout << "差集(set1 - set2):";
    for (int v : diffSet) cout << v << " ";
    cout << endl;

    return 0;
}

实例4:使用栈判断括号匹配

题目:输入一个字符串,包含 ()[]{},判断括号是否匹配。

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

bool isMatching(char open, char close) {
    return (open == '(' && close == ')') ||
           (open == '[' && close == ']') ||
           (open == '{' && close == '}');
}

bool checkBrackets(const string &s) {
    stack<char> st;
    for (char c : s) {
        if (c == '(' || c == '[' || c == '{') {
            st.push(c);
        } else if (c == ')' || c == ']' || c == '}') {
            if (st.empty() || !isMatching(st.top(), c)) {
                return false;
            }
            st.pop();
        }
    }
    return st.empty();
}

int main() {
    string expr;
    cout << "请输入表达式:";
    cin >> expr;
    if (checkBrackets(expr)) {
        cout << "括号匹配" << endl;
    } else {
        cout << "括号不匹配" << endl;
    }
    return 0;
}

10.7 阶段性编程练习

练习1:vector练习

输入n个整数,存入vector,然后删除所有偶数,输出剩下的奇数。

练习2:list练习

创建一个list,存放10个随机整数(1-100)。使用list的sort排序,然后反转,输出结果。

练习3:map电话簿

实现一个简单的电话簿,用map存储姓名和电话号码。支持添加、删除、查找、显示所有联系人。

练习4:set去重

输入一串整数,可能有重复,用set去除重复后按升序输出。

练习5:队列模拟

用queue模拟一个打印任务队列。每个任务有名称和页数,按先进先出处理,并输出处理顺序。

练习6:算法综合

生成一个vector包含20个随机整数(1-100),然后:
- 排序
- 查找是否存在50
- 统计大于50的个数
- 删除所有小于30的元素(提示:用erase配合remove_if)


10.8 第10章编程作业

恭喜你学完了STL!现在来挑战几个综合题目,用STL容器和算法解决问题。

作业1:学生成绩管理系统(STL版)

用vector存储学生信息(结构体包含姓名、学号、各科成绩)。实现:
- 添加学生
- 删除学生(按学号)
- 修改成绩
- 按总成绩排序并输出
- 按学号查找
- 统计各科平均分、最高分、最低分

作业2:文章词频统计

读入一篇文章(可以从文件读或手动输入),统计每个单词出现的次数,忽略大小写和标点,最后按词频降序输出前10个单词。

作业3:迷宫最短路径(选做)

用queue实现广度优先搜索(BFS)求解迷宫最短路径。迷宫用二维vector表示,0可走,1障碍。

作业4:多项式计算器

用map存储多项式(指数为键,系数为值)。实现两个多项式的加法、减法、乘法。

作业5:停车场模拟

用stack模拟停车场,queue模拟等待车道。车辆到达时,如果有空位则停入stack,否则进入queue;车辆离开时,需要将上面的车暂时移出(用另一个stack),然后让目标车离开,再移回。输出每次操作后的状态。