S4 vector 与 stack(20 分钟补丁)

这是什么: 两个 STL 容器。vector 是"长度可以变的数组",stack 是"后进先出的桶"。

为什么现在补: L07 教了 queue,但同一族的 vectorstack 一直没提。vector长度事先不知道的题里比定长数组省心得多(CSP-J 2019 T2 公交换乘就是这种),而且它是后面存图的标准工具;stack 是 NOI 大纲入门级明文要求的(2.1.3-1)。

前置: L07 的 queue——stack 的用法和它几乎一模一样。


一、最小可用模板

1.1 vector:长度可变的数组

#include <bits/stdc++.h>
using namespace std;

int main() {
    vector<int> v;                 // 空的,长度 0

    v.push_back(10);               // 往末尾加一个
    v.push_back(20);
    v.push_back(30);

    cout << v.size() << endl;      // 3
    cout << v[1] << endl;          // 20  —— 下标访问和普通数组一样

    for (int i = 0; i < (int)v.size(); i++) {
        cout << v[i] << " ";
    }
    cout << endl;                  // 10 20 30

    v.pop_back();                  // 删掉末尾一个
    cout << v.size() << endl;      // 2

    v.clear();                     // 全清空
    cout << v.size() << endl;      // 0
    return 0;
}
写法 作用
vector<int> v; 建一个空的
vector<int> v(n); 建 n 个,全是 0
vector<int> v(n, -1); 建 n 个,全填 -1
v.push_back(x) 末尾加一个
v.pop_back() 末尾删一个(不返回值)
v.size() 现在有几个
v[i] 第 i 个(从 0 开始)
v.empty() 是不是空的
v.clear() 全清空

⚠️ v.size() 的类型是无符号整数。i < v.size() 里如果 iint,编译器会警告;更要命的是 v.size() - 1v 为空时会变成一个巨大的正数,而不是 -1。

📝 统一写成 (int)v.size(),把它转成普通 int 再用,一劳永逸。

vector 可以直接整个排序,和数组一样:

#include <bits/stdc++.h>
using namespace std;

int main() {
    vector<int> v;
    v.push_back(30);
    v.push_back(10);
    v.push_back(20);

    sort(v.begin(), v.end());      // 注意:数组写 sort(a, a+n),vector 写 begin/end
    for (int i = 0; i < (int)v.size(); i++) cout << v[i] << " ";
    cout << endl;                  // 10 20 30
    return 0;
}

📝 什么时候用 vector、什么时候用普通数组? 长度已知且题目给了上限(n ≤ 10^5)就用普通数组,开在 main 外面更省事;长度事先不知道、要一个一个往里塞的时候用 vector

1.2 stack:后进先出

和 L07 的 queue 是一对:队列是排队(先来的先走),栈是摞盘子(后放的先拿)。

#include <bits/stdc++.h>
using namespace std;

int main() {
    stack<int> st;

    st.push(1);
    st.push(2);
    st.push(3);

    while (!st.empty()) {
        cout << st.top() << " ";   // top() 只看不删
        st.pop();                  // pop() 只删不返回
    }
    cout << endl;                  // 3 2 1  —— 和放进去的顺序正好相反
    return 0;
}
queue(L07 已学) stack(本补丁) 区别
q.push(x) st.push(x) 一样
q.front() st.top() 队列看队头,栈看栈顶
q.pop() st.pop() 都是只删不返回
q.empty() / q.size() st.empty() / st.size() 一样

📝 L07 关于 queue 的那条铁律对 stack 一字不改:top() 只看不删、pop() 只删不返回,要取值必须先 int x = st.top(); st.pop(); 两步走。

栈最典型的用途是括号匹配

#include <bits/stdc++.h>
using namespace std;

int main() {
    string s = "(()())";
    stack<char> st;
    bool ok = true;

    for (int i = 0; i < (int)s.size(); i++) {
        if (s[i] == '(') {
            st.push(s[i]);
        } else {
            if (st.empty()) { ok = false; break; }   // 右括号多了
            st.pop();
        }
    }
    if (!st.empty()) ok = false;                     // 左括号多了

    cout << (ok ? "yes" : "no") << endl;             // yes
    return 0;
}

二、两个坑

⚠️ 坑 1:vectorpush_back 过就用下标,是越界。

vector<int> v;
v[0] = 5;                 // 错!v 里一个元素都没有

编译能过,运行时是未定义行为——可能崩溃,也可能"看起来正常"然后给出错误答案,后者更难查。

vector<int> v;
v.push_back(5);           // 对:先塞进去
// 或者
vector<int> w(10);        // 对:一次开 10 个,w[0]~w[9] 都能用

⚠️ 坑 2:栈空的时候调 top(),和 L07 队列空时调 front() 是同一个坑。

不报错,返回一堆垃圾数据,答案莫名其妙。

📝 凡是取 top() / front() 之前,先确认 !empty() 上面括号匹配那段之所以要写 if (st.empty()) { ok = false; break; },就是在防这个。


三、配套真题

CSP-J 2019 T2 公交换乘(洛谷 P5661) —— 坐地铁会得到一张 45 分钟内有效的优惠券,可以免掉一次票价不超过该地铁票价的公交车费;券会累积,用的时候优先用最早得到的那张。求总花费。

"券会累积、优先用最早的"这句话直接对应一个队列式的结构,但券还会过期、还要挑价格够的,所以简单的 queue 不够用——用 vector 存所有券、配一个"从哪开始找"的指针会顺手得多。数据范围 n ≤ 10⁵,想清楚你的写法会不会退化成 O(n²)。

思路提示见 S8_真题分级提示.md,代码自己写。

stack 在 CSP-J 里主要出现在 T3 的表达式类题目(2020 T3 表达式、2022 T3 逻辑表达式)。那是本次冲刺目标之外的难度,先会用,遇到再说


四、30 秒自测

  1. vector<int> v(5); 建出来的 5 个元素初始值是多少?
  2. stack 取栈顶元素并弹出,为什么不能写成一句 int x = st.pop();
  3. for (int i = 0; i < v.size() - 1; i++)v 为空时会发生什么?
答案
  1. 全是 0。(vector<int> v(5, -1); 则全是 -1)
  2. pop() 的返回类型是 void,只负责删除。必须两步:int x = st.top(); st.pop();——和 L07 的 queue 完全一样。
  3. v.size() 是无符号整数,0 - 1 不会得到 -1,而是一个极大的正数,循环会一直跑下去并越界。写成 (int)v.size() - 1

以后学: setmappriority_queue 属于提高级(大纲 2.2.2),本次冲刺不需要。想去重和查询"有没有出现过"时,用 S3 的计数数组就够了。