C++9(vector)

**📚 超贴心目录

1.🎬 开场白:为啥 vector 是新手必学?

2.🧱 基础打底:vector 是个啥?有啥超能力?

3✨ 核心技能:手把手教你用 vector(接口篇)

3.1 开局第一步:怎么创建 vector?(构造函数)

3.2 遍历神器:迭代器咋用不翻车?

3.3 空间管理:size 和 capacity 别再搞混了!

3.4 增删查改:新手常用操作全拆解

4.⚠️ 避坑指南:迭代器失效 = 程序崩溃?手把手教你躲坑!

5.🚀 实战练兵:OJ 题用 vector 秒解(新手能看懂的解法)

6.🧐 刨根问底:vector 底层到底是个啥?(新手版)

7.📝 通关总结:核心知识点 + 复习口诀

1.🎬 开场白:为啥 vector 是新手必学?

作为 C++ 新手,你是不是刚学完数组就被 "数组大小固定、越界就崩溃" 折磨得头疼?是不是想找一个 "能自动变大变小、用起来像数组一样简单" 的容器?

**那 vector 就是你的 "救星"!**它是 C++ STL 里最基础、最常用的动态数组,学会它不仅能搞定日常编程,还能轻松应对 OJ 刷题、课程设计,甚至是入门级面试 ------ 可以说,vector 是新手从 "只会写数组" 到 "能用 STL" 的第一个关键台阶。

🧱 基础打底:vector 是个啥?有啥超能力?
2.1 先讲明白:vector 的本质

你可以把 vector 理解成 "智能版的数组":

  • 像普通数组一样,支持[]下标访问(比如v[0]取第一个元素),随机访问速度贼快;
  • 比数组牛的是:它能 "自动长大"------ 你往里面加元素,它会自己扩容,不用你手动申请内存;
  • 但它也有小缺点:如果在中间插 / 删元素,后面的元素要 "挪位置",效率会低一点(新手先记住 "尾插尾删最香" 就行)。

2.2 必知:vector 的扩容小秘密

新手不用深究底层,但要记住两个关键:

  • 扩容不是 "加一个元素扩一次":vector 会提前多申请一些空间(叫 "备用空间"),只有当实际元素个数(size)等于总空间(capacity)时,才会触发扩容
  • 扩容倍数有讲究:
    VS 编译器(新手常用):扩到原来的1.5 倍 (比如原来能存 10 个,扩容后能存 15 个);
    G++ 编译器:扩到原来的2 倍(原来 10 个,扩容后 20 个);
  • 扩容有 "小代价":扩容时会先开一块新空间,把旧元素搬过去,再删掉旧空间 ------ 所以提前知道要存多少元素的话,能手动预留空间,避免频繁扩容(后面会讲)。

✨ 核心技能:手把手教你用 vector(接口篇)
3.1 开局第一步:怎么创建 vector?(构造函数)

先掌握 4 种最常用的创建方式,:

cpp 复制代码
#include <iostream>
#include <vector> // 用vector必须加这个头文件!新手别忘
using namespace std; // 新手先加这个,避免写std::vector

int main() {
    // 方式1:空vector(最常用!先创建,后面再加元素)
    vector<int> v1; 
    // 新手提示:int可以换成double、string,比如vector<string> v_str;
    
    // 方式2:创建时直接初始化n个相同元素(比如5个3)
    vector<int> v2(5, 3); 
    cout << "v2的元素:";
    for (int i = 0; i < v2.size(); i++) {
        cout << v2[i] << " "; // 输出:3 3 3 3 3
    }
    cout << endl;
    
    // 方式3:用已有的vector拷贝(复制一份一模一样的)
    vector<int> v3(v2); 
    cout << "v3的元素和v2一样:";
    for (int e : v3) { // 新手也可以学这种范围for,更简单
        cout << e << " ";
    }
    cout << endl;
    
    // 方式4:用数组初始化(新手常把数组转vector用)
    int a[] = {1,2,3,4};
    vector<int> v4(a, a+4); // 从a[0]到a[3],共4个元素
    cout << "v4的元素:";
    for (auto e : v4) { // auto新手可以理解成"让编译器自己判断类型"
        cout << e << " "; // 输出:1 2 3 4
    }
    cout << endl;
    
    return 0;
}

小提醒

  • 创建 vector 时要指定类型(比如vector),不能直接写vector;
  • 方式 1 是新手最常用的,因为很多时候一开始不知道要存多少元素。

3.2 遍历神器:迭代器咋用不翻车?

新手可能觉得 "迭代器" 听起来高级,其实它就是 "能遍历 vector 的指针替代品",核心记住两种遍历方式就行:

cpp 复制代码
vector<int> v{1,2,3,4}; // 新手注意:这种初始化方式C++11及以上支持
// 迭代器的写法:vector<类型>::iterator
vector<int>::iterator it = v.begin(); // begin指向第一个元素
while (it != v.end()) { // end指向最后一个元素的下一个位置(不是最后一个!)
    cout << *it << " "; // *it取迭代器指向的元素,像指针一样
    ++it; // 迭代器往后走一步
}
// 输出:1 2 3 4

3.2.2 反向迭代器(反向遍历)

cpp 复制代码
vector<int> v{1,2,3,4};
// 反向迭代器写法:vector<类型>::reverse_iterator
vector<int>::reverse_iterator it = v.rbegin(); // rbegin指向最后一个元素
while (it != v.rend()) { // rend指向第一个元素的前一个位置
    cout << *it << " ";
    ++it; // 反向迭代器++是往前跑
}
// 输出:4 3 2 1

避坑点:

  • end()和rend()都不指向有效元素,千万别解引用(比如*v.end()会崩溃);
  • 新手如果觉得迭代器麻烦,先用for(int i=0; i<v.size(); i++)或范围 for(for(auto e : v)),足够应付大部分场景。

3.3 空间管理:size 和 capacity 别再搞混了!

新手最容易把 "实际元素个数" 和 "总空间大小" 搞混,用表格和代码帮你彻底分清:

cpp 复制代码
#include <vector>
#include <iostream>
using namespace std;

int main() {
    vector<int> v;
    // 先看初始状态:空vector
    cout << "初始size:" << v.size() << endl; // 0
    cout << "初始capacity:" << v.capacity() << endl; // VS下是0,G++下可能是0/1
    cout << "是否为空:" << boolalpha << v.empty() << endl; // true(boolalpha让输出是true/false)
    
    // 尾插5个元素
    for (int i = 0; i < 5; i++) {
        v.push_back(i);
    }
    cout << "插5个元素后size:" << v.size() << endl; // 5
    cout << "插5个元素后capacity:" << v.capacity() << endl; // VS下是8,G++下是8(2倍扩容)
    
    // resize:改元素个数
    v.resize(8, 0); // 改成8个元素,新增的3个填0
    cout << "resize后size:" << v.size() << endl; // 8
    cout << "resize后capacity:" << v.capacity() << endl; // 还是8(没超容量)
    cout << "第6个元素:" << v[5] << endl; // 0(新增的元素)
    
    // reserve:预留空间
    v.reserve(10);
    cout << "reserve后size:" << v.size() << endl; // 还是8(只开空间,不填元素)
    cout << "reserve后capacity:" << v.capacity() << endl; // 10
    return 0;
}

核心技巧:

  • 如果提前知道要存 100 个元素,先v.reserve(100),避免插入时频繁扩容(新手写循环插元素时,加这一行能让代码快很多);
  • resize会改变能访问的元素个数(比如 resize (8) 后,v [7] 是有效的),reserve不会(reserve (10) 后,v [8] 还是越界)。

3.4 增删查改 常用操作全拆解

新手用 vector,80% 的操作都是 "尾插、尾删、查元素、改元素",直接上代码 + 新手注释:

cpp 复制代码
#include <vector>
#include <iostream>
#include <algorithm> // find函数需要这个头文件!新手别忘
using namespace std;

int main() {
    vector<int> v{1,2,3,4};
    
    // 1. 尾插(最常用!新手优先用这个)
    v.push_back(5); 
    cout << "尾插5后:";
    for (auto e : v) cout << e << " "; // 1 2 3 4 5
    cout << endl;
    
    // 2. 尾删(也常用,删最后一个元素)
    v.pop_back();
    cout << "尾删后:";
    for (auto e : v) cout << e << " "; // 1 2 3 4
    cout << endl;
    
    // 3. 查找元素(新手注意:vector没有自带find,要用算法库的)
    // find(起始迭代器, 结束迭代器, 要找的值)
    auto pos = find(v.begin(), v.end(), 3);
    if (pos != v.end()) { // 找到才操作!新手别漏这个判断
        cout << "找到3啦,位置是:" << pos - v.begin() << endl; // 位置2(从0数)
    } else {
        cout << "没找到3" << endl;
    }
    
    // 4. 插入元素(新手尽量少用中间插入,效率低)
    if (pos != v.end()) {
        v.insert(pos, 10); // 在3的前面插10
        cout << "插入10后:";
        for (auto e : v) cout << e << " "; // 1 2 10 3 4
        cout << endl;
    }
    
    // 5. 删除元素(新手注意:erase后迭代器会失效,要重新赋值)
    pos = find(v.begin(), v.end(), 10); // 重新找10的位置
    if (pos != v.end()) {
        v.erase(pos); // 删除10
        cout << "删除10后:";
        for (auto e : v) cout << e << " "; // 1 2 3 4
        cout << endl;
    }
    
    // 6. 改元素(直接用[],和数组一样)
    v[2] = 99; // 把第三个元素(位置2)改成99
    cout << "修改后第三个元素:" << v[2] << endl; // 99
    
    // 7. 清空所有元素(新手常用)
    v.clear();
    cout << "清空后size:" << v.size() << endl; // 0
    cout << "清空后capacity:" << v.capacity() << endl; // 还是原来的大小(只删元素,不缩空间)
    return 0;
}

小口诀:

  • 插删优先用 "尾插尾删"(push_back/pop_back),中间插删尽量少用;
  • 查找元素要加 "是否找到" 的判断,不然会操作无效迭代器;
  • 清空用 clear (),但 clear 只删元素,不释放空间(新手暂时不用管释放,编译器会自动处理)。

4.⚠️ 避坑指南:迭代器失效 = 程序崩溃?手把手教你躲坑!

迭代器失效是新手最容易踩的坑,比如写着写着程序突然崩溃,大概率是这个原因!先给新手讲明白 "为啥失效",再教 "怎么躲坑"。

4.1 新手能懂的 "迭代器失效" 定义

迭代器本质是 "指向 vector 底层数组的指针",如果这个指针指向的空间被销毁了,或者指向的位置不对了,再用这个迭代器,就会 "失效"------ 好比你记了朋友家的地址,结果朋友搬家了,你还按旧地址找,肯定找不到(程序就崩溃)。

4.2 两种常见失效场景(必记)
场景 1:扩容导致 "全局失效"(所有迭代器都不能用了)

只要 vector 扩容(比如 push_back、reserve、resize、insert 导致容量不够),底层会开新空间、删旧空间,原来的迭代器就指向了 "被

删掉的旧空间",再用就崩溃。

错误示例

cpp 复制代码
vector<int> v{1,2,3,4};
auto it = v.begin(); // 记录第一个元素的迭代器
v.reserve(100); // 扩容,旧空间被删
cout << *it << endl; // 崩溃!it指向的空间没了

正确示例

cpp 复制代码
vector<int> v{1,2,3,4};
auto it = v.begin();
v.reserve(100);
it = v.begin(); // 扩容后重新给迭代器赋值!
cout << *it << endl; // 正常输出1

场景 2:erase 导致 "局部失效"(被删位置的迭代器不能用)

erase 删除元素后,后面的元素会往前挪,被删位置的迭代器就 "空了",如果直接 ++ 会越界。

错误示例

cpp 复制代码
vector<int> v{1,2,3,4};
auto it = v.begin();
while (it != v.end()) {
    if (*it % 2 == 0) {
        v.erase(it); // 删除后it失效了!
    }
    ++it; // 失效的it++,直接崩溃
}

正确示例

cpp 复制代码
vector<int> v{1,2,3,4};
auto it = v.begin();
while (it != v.end()) {
    if (*it % 2 == 0) {
        it = v.erase(it); // erase返回下一个有效迭代器,重新赋值!
    } else {
        ++it; // 没删除才++
    }
}
// 最终v:{1,3},正常运行

4.3 避坑总结

  • 扩容后:所有迭代器重新赋值;
  • erase 后:用 erase 的返回值更新迭代器;
  • 如果怕麻烦,扩容 /erase 后直接重新遍历(比如用 for 循环),别复用之前的迭代器。

🚀 实战练兵:OJ 题用 vector 秒解(新手能看懂的解法)

学会了基础,就得刷题巩固!选两道新手能独立完成的经典 OJ 题,用 vector 实现

5.1 题目 1:只出现一次的数字(简单题)
题目描述 :给定一个非空整数数组,除了一个元素只出现一次,其余每个元素都出现两次,找出这个只出现一次的元素。
思路:不用复杂算法,先排序,再遍历找 "只出现一次" 的元素(新手能理解的解法)。

cpp 复制代码
#include <vector>
#include <iostream>
#include <algorithm> // sort需要这个头文件
using namespace std;

int main() {
    // 新手输入:先输入数组长度,再输入元素
    int n;
    cout << "请输入数组长度:";
    cin >> n;
    vector<int> nums(n); // 创建n个元素的vector
    cout << "请输入" << n << "个整数:";
    for (int i = 0; i < n; i++) {
        cin >> nums[i]; // 输入元素
    }
    
    // 新手解法:排序后遍历
    sort(nums.begin(), nums.end()); // 排序,相同元素会挨在一起
    int res = nums[0]; // 初始化结果
    for (int i = 1; i < nums.size(); i++) {
        if (nums[i] == nums[i-1]) {
            // 相同的话,跳过下一个元素
            i++;
        } else {
            res = nums[i];
        }
    }
    
    cout << "只出现一次的数字是:" << res << endl;
    return 0;
}

提示:

  • sort 排序后,相同元素会相邻,只要找到 "和前一个不一样" 的元素就行;
  • 这道题还有更高效的异或解法(前面提过),新手先掌握这种 "直观解法",后续再学优化。

5.2 题目 2:杨辉三角(简单题)

题目描述:给定一个非负整数 numRows,生成杨辉三角的前 numRows 行。
思路

  • 杨辉三角每行第一个和最后一个元素是 1;
  • 中间元素 = 上一行的 "前一个元素 + 当前位置元素";
  • 用二维 vector 存储,每行先 resize,再填值。
cpp 复制代码
#include <vector>
#include <iostream>
using namespace std;

int main() {
    int numRows;
    cout << "请输入杨辉三角的行数:";
    cin >> numRows;
    
    // 二维vector:外层存行,内层存每行的元素
    vector<vector<int>> yanghui(numRows);
    
    // 新手分步写:先填每行的元素
    for (int i = 0; i < numRows; i++) {
        // 第i行有i+1个元素,resize初始化全为1
        yanghui[i].resize(i + 1, 1);
        
        // 中间元素(从第2行开始才有)
        if (i >= 2) { // 第0行、第1行不用改,全是1
            for (int j = 1; j < i; j++) { // j从1到i-1(跳过首尾)
                yanghui[i][j] = yanghui[i-1][j-1] + yanghui[i-1][j];
            }
        }
    }
    
    // 输出杨辉三角(新手美化输出)
    cout << "杨辉三角前" << numRows << "行:" << endl;
    for (int i = 0; i < numRows; i++) {
        // 先输空格,让三角更美观(新手可选)
        for (int k = 0; k < numRows - i - 1; k++) {
            cout << " ";
        }
        // 输出每行元素
        for (int j = 0; j < yanghui[i].size(); j++) {
            cout << yanghui[i][j] << " ";
        }
        cout << endl;
    }
    return 0;
}

收获:

  • 二维 vector 就是 "vector 里面套 vector",可以理解成 "行数可变的二维数组";
  • resize 不仅能改大小,还能填初始值,新手用这个能少写很多循环。

6.🧐 刨根问底:vector 底层到底是个啥?(新手版)

新手不用深入源码,但要知道 vector 的底层核心,能帮你理解 "为啥扩容、为啥迭代器失效":

6.1 核心结构:三个指针

vector 底层靠三个指针管理一块连续的数组:

  • _start:指向数组第一个元素(新手理解成 "数组名" 就行);
  • _finish:指向最后一个有效元素的下一个位置(所以 size = _finish - _start);
  • _end_of_storage:指向数组最后一个空间的下一个位置(所以 capacity = _end_of_storage - _start)。

6.2 能懂的 "扩容过程"

当 push_back 元素,发现_finish == _end_of_storage(容量满了),就会:

1.开一块新空间(大小是原来的 1.5/2 倍);

2.把旧空间的元素 "搬" 到新空间(新手理解成 "复制过去");

3.删掉旧空间,把三个指针指向新空间;

4.把新元素插入到新空间的末尾。

6.3 别踩坑:memcpy 浅拷贝坑(简单提)

新手暂时不用写 vector 的底层,但要知道:如果 vector 存的是 string、自定义类这些 "带资源" 的类型,不能用 memcpy 复制(会导致重复释放内存),要用 "逐个拷贝"------ 这也是为啥 STL 的 vector 能安全存各种类型的原因。

📝 通关总结:核心知识点 + 复习口诀

7.1 核心知识点(新手记这 5 条就够)

  • 本质:动态连续数组,随机访问快,尾插尾删快,中间插删慢;
  • 扩容:VS1.5 倍、G++2 倍,reserve 提前预留空间能提速;
  • 接口:push_back/pop_back(尾插尾删)、size/capacity(大小 / 容量)、resize/reserve(改大小 / 预留空间)、erase/insert(删 / 插,注意迭代器失效);
  • 迭代器失效:扩容后全局失效、erase 后局部失效,解决方法是 "重新赋值迭代器";
  • 二维 vector:数组的数组,每行独立,能动态调整大小。

7.2 复习口诀

扩容要 reserve,改数用 resize;

插删优先尾,迭代器要复位;

size 是个数,capacity 是仓库;

遍历用 for,新手不迷路。

相关推荐
覆东流1 小时前
第5天:Python字符串操作进阶
开发语言·后端·python
吴梓穆1 小时前
UE5 C++ 使C++创建动画蓝图
开发语言·c++·ue5
冰暮流星1 小时前
javascript之表单事件1
开发语言·前端·javascript
0xDevNull1 小时前
队列(Queue)实战教程:从原理到架构应用
java·开发语言·后端
conti1231 小时前
水题记录2.4
c++·笔记·题解
ShineWinsu1 小时前
C++技术文章
开发语言·c++
星星码️2 小时前
C++选择题练习(二)
c++
再写一行代码就下班2 小时前
word模版导出(占位符方式)
java·开发语言·word
~无忧花开~2 小时前
CSS全攻略:从基础到实战技巧
开发语言·前端·css·学习·css3